Merge branch 'feature/LIL-680' into 'master'

Feature/lil 680

See merge request lilcity/backend!210
remotes/origin/feature/LIL-711
cfwme 7 years ago
commit 741c08a476
  1. 25
      apps/course/templates/course/course.html
  2. 25
      apps/course/templates/course/course_only_lessons.html
  3. 10
      apps/notification/tasks.py
  4. 112
      apps/notification/templates/notification/email/gift_certificate.html
  5. 23
      apps/notification/utils.py
  6. 7
      apps/payment/admin.py
  7. 34
      apps/payment/migrations/0025_giftcertificate_usergiftcertificate.py
  8. 39
      apps/payment/migrations/0026_auto_20181101_1546.py
  9. 22
      apps/payment/migrations/0027_auto_20181109_1402.py
  10. 21
      apps/payment/migrations/0028_add_gift_certificates.py
  11. 38
      apps/payment/models.py
  12. 17
      apps/payment/templates/payment/gift_certificate.html
  13. 15
      apps/payment/templates/payment/gift_certificate_get.html
  14. 36
      apps/payment/templates/payment/gift_certificate_item.html
  15. 4
      apps/payment/templates/payment/gift_certificate_items.html
  16. 13
      apps/payment/templates/payment/gift_certificate_payment_success.html
  17. 35
      apps/payment/templates/payment/gift_certificates.html
  18. 11
      apps/payment/templates/payment/payment_success.html
  19. 101
      apps/payment/views.py
  20. 3
      apps/school/templates/summer/promo.html
  21. 13
      apps/user/templates/user/profile.html
  22. 3
      apps/user/views.py
  23. 4
      project/templates/blocks/header.html
  24. 1
      project/templates/blocks/lil_store_js.html
  25. 24
      project/templates/blocks/popup_enter_gift_code.html
  26. 31
      project/templates/blocks/popup_gift_certificate.html
  27. 9
      project/templates/blocks/promo.html
  28. 4
      project/templates/lilcity/index.html
  29. 9
      project/urls.py
  30. 13
      project/views.py
  31. BIN
      web/src/img/clock.png
  32. BIN
      web/src/img/done.png
  33. BIN
      web/src/img/fb.png
  34. BIN
      web/src/img/gift-certificates/1000.jpg
  35. BIN
      web/src/img/gift-certificates/10000.jpg
  36. BIN
      web/src/img/gift-certificates/2000.jpg
  37. BIN
      web/src/img/gift-certificates/3000.jpg
  38. BIN
      web/src/img/gift-certificates/5000.jpg
  39. BIN
      web/src/img/instagram.png
  40. BIN
      web/src/img/twitter.png
  41. 4
      web/src/js/modules/common.js
  42. 21
      web/src/js/modules/popup.js
  43. 95
      web/src/sass/_common.sass
  44. 9
      web/webpack.config.js

@ -29,17 +29,22 @@
<a class="go__btn btn btn_light-gray" href="{% url 'course_edit' course.id %}">Редактировать</a>
{% endif %}
{% if course.author != request.user and not paid and course.price %}
<a href="#"
class="go__btn btn{% if pending %} btn_gray{% endif %} btn_md"
{% if user.is_authenticated %}
{% if not pending %}
data-course-buy
data-popup=".js-popup-course-buy"
{% endif %}
{% else %}
data-popup=".js-popup-auth"
<div>
<a href="#"
class="go__btn btn{% if pending %} btn_gray{% endif %} btn_md"
{% if user.is_authenticated %}
{% if not pending %}
data-course-buy
data-popup=".js-popup-course-buy"
{% endif %}
{% 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 %}
>{% if pending %}ОЖИДАЕТСЯ ПОДТВЕРЖДЕНИЕ ОПЛАТЫ{% else %}КУПИТЬ КУРС{% endif %}</a>
</div>
{% endif %}
</div>
<div

@ -29,17 +29,22 @@
<a class="go__btn btn" href="{% url 'course_edit' course.id %}">Редактировать</a>
{% endif %}
{% if not paid and course.price and not has_full_access %}
<a
class="go__btn btn{% if pending %} btn_gray{% endif %} btn_md"
{% if user.is_authenticated %}
{% if not pending %}
href="{% url 'course-checkout' course.id %}"
{% endif %}
{% else %}
data-popup=".js-popup-auth"
href="#"
<div>
<a
class="go__btn btn{% if pending %} btn_gray{% endif %} btn_md"
{% if user.is_authenticated %}
{% if not pending %}
href="{% url 'course-checkout' course.id %}"
{% endif %}
{% else %}
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 %}
>{% if pending %}ОЖИДАЕТСЯ ПОДТВЕРЖДЕНИЕ ОПЛАТЫ{% else %}КУПИТЬ КУРС{% endif %}</a>
</div>
{% endif %}
</div>
<div

@ -8,7 +8,7 @@ from django.db.models import Max
from apps.notification.models import UserNotification
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.utils.db import format_sql, execute_sql
from project.sengrid import get_sendgrid_client
@ -99,3 +99,11 @@ def sendgrid_update_recipients():
sg = get_sendgrid_client()
response = sg.client.contactdb.recipients.patch(request_body=data)
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)

@ -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 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()

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

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

@ -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">
<a class="done__btn btn btn_md btn_stroke" href="{% url 'school:school' %}">ПЕРЕЙТИ К ШКОЛЕ</a>
</div>
{% else %}
{% endif %}
{% if course %}
<div class="done__title title">Вы успешно приобрели курс!</div>
<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>
{% endif %}
</div>

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

@ -20,6 +20,9 @@
{% if is_purchased %}ваша подписка истекает {{ subscription_ends_humanize }}<br/>перейти к оплате{% 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 class="main__school school school_main">
<div class="school__col">

@ -2,7 +2,10 @@
<div class="section">
<div class="section__center center">
<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">
{% thumbnail user.photo "120x120" crop="center" as im %}
<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>
</div>
{% 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 %}
<div class="center center_xs">
<div class="done">
<div class="done__title">Вы пока ничего не приобрели...</div>

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

@ -41,6 +41,10 @@
<div class="header__group">
<a class="header__section" href="https://blog.lil.school">БЛОГ</a>
</div>
<div class="header__group">
<a class="header__section {% active_link 'gift-certificates' %}"
href="{% url 'gift-certificates' %}">СЕРТИФИКАТЫ</a>
</div>
</nav>
</div>
{% include 'templates/blocks/user_menu.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" }},
},
};
</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 %}
class="main__btn btn"
>{% 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>
{% elif user.is_authenticated and online_coming_soon and school_schedule and school_schedule.start_at_humanize %}
<div class="">
@ -45,6 +49,10 @@
{% endif %}
class="main__btn btn"
>{% 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>
{% else %}
<div class="main__subtitle">
@ -59,6 +67,7 @@
>
купить доступ от {{ min_school_price }} руб./месяц
</a>
<a class="main__btn btn btn_stroke-black" href="{% url 'gift-certificates' %}">Подарить другу</a>
{% else %}
<a class="main__btn btn btn_white" href="{% url 'school:school' %}">Подробнее</a>
{% endif %}

@ -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" %}
</div>

@ -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/<str:slug>/', ContestView.as_view(), name='contest'),
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('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.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,

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

@ -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() {

@ -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');

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

@ -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]'

Loading…
Cancel
Save