diff --git a/apps/course/templates/course/course.html b/apps/course/templates/course/course.html
index 7ab898b1..72b3df68 100644
--- a/apps/course/templates/course/course.html
+++ b/apps/course/templates/course/course.html
@@ -29,17 +29,22 @@
Редактировать
{% endif %}
{% if course.author != request.user and not paid and course.price %}
-
+ {% if pending %}ОЖИДАЕТСЯ ПОДТВЕРЖДЕНИЕ ОПЛАТЫ{% else %}КУПИТЬ КУРС{% endif %}
+ {% if not paid %}
+ Подарить другу
{% endif %}
- >{% if pending %}ОЖИДАЕТСЯ ПОДТВЕРЖДЕНИЕ ОПЛАТЫ{% else %}КУПИТЬ КУРС{% endif %}
+
{% endif %}
Поздравляем с успешной
+ покупкой!
+
+
Вы получаете {{ gift_certificate.price }} лиликов на счет! 1 LIL = 1 руб.
+ Накапливайте монеты и тратьте их на оплату школы и курсов.
+
+
+
+
+
+
+
+
+ | Подарочный сертификат
+ |
+ {{ gift_certificate.price }} р.
+ |
+
+
+
+
+
+
Чтобы воспользоваться сертификатом, перейдите по ссылке
+
+
+ Или воспользуйтесь сертификатом, введя уникальный код на в разделе
+ вашего профиля на сайте
lil.school
+
+
Ваш код
+
+
+
{{ user_gift_certificate.code }}
+
+
+
+ Вы так же можете отправить это письмо, ссылку или код вашему другу, чтобы подарить ему этот сертификат.
+
+
+ Присоединяйтесь к нам!
+
+
+ {% if config.SERVICE_TWITTER_URL %}
+
+
+
+ {% endif %}
+ {% if config.SERVICE_FB_URL %}
+
+
+
+ {% endif %}
+ {% if config.SERVICE_INSTAGRAM_URL %}
+
+
+
+ {% endif %}
+
+
+{% endblock content %}
diff --git a/apps/notification/utils.py b/apps/notification/utils.py
index d556bd96..07510c41 100644
--- a/apps/notification/utils.py
+++ b/apps/notification/utils.py
@@ -1,16 +1,27 @@
+from django.contrib.staticfiles.storage import staticfiles_storage
from twilio.rest import Client
-
-from django.core.mail import EmailMessage
+from django.core.mail import EmailMessage, EmailMultiAlternatives
+from anymail.message import attach_inline_image_file
from django.conf import settings
from django.template.loader import get_template
+
from project.celery import app
@app.task
-def send_email(subject, to_email, template_name, attachments=[], **kwargs):
- html = get_template(template_name).render(kwargs)
- email = EmailMessage(subject, html, to=[to_email], attachments=attachments)
- email.content_subtype = 'html'
+def send_email(subject, to_email, template_name, attachments=[], inline_images=[], **kwargs):
+ if inline_images:
+ email = EmailMultiAlternatives(subject, '', to=[to_email], attachments=attachments)
+ context = kwargs
+ for name, path in inline_images:
+ cid = attach_inline_image_file(email, staticfiles_storage.path(path))
+ context[name] = cid
+ html = get_template(template_name).render(context)
+ email.attach_alternative(html, "text/html")
+ else:
+ html = get_template(template_name).render(kwargs)
+ email = EmailMessage(subject, html, to=[to_email], attachments=attachments)
+ email.content_subtype = 'html'
email.send()
diff --git a/apps/payment/admin.py b/apps/payment/admin.py
index 19166563..b26b03a2 100644
--- a/apps/payment/admin.py
+++ b/apps/payment/admin.py
@@ -5,7 +5,7 @@ from polymorphic.admin import (
PolymorphicChildModelFilter,
)
-from .models import AuthorBalance, CoursePayment, SchoolPayment, Payment
+from .models import AuthorBalance, CoursePayment, SchoolPayment, Payment, GiftCertificate
@admin.register(AuthorBalance)
@@ -61,3 +61,8 @@ class PaymentAdmin(PolymorphicParentModelAdmin):
CoursePayment,
SchoolPayment,
)
+
+
+@admin.register(GiftCertificate)
+class GiftCertificateAdmin(admin.ModelAdmin):
+ pass
diff --git a/apps/payment/migrations/0025_giftcertificate_usergiftcertificate.py b/apps/payment/migrations/0025_giftcertificate_usergiftcertificate.py
new file mode 100644
index 00000000..3f9dcc34
--- /dev/null
+++ b/apps/payment/migrations/0025_giftcertificate_usergiftcertificate.py
@@ -0,0 +1,34 @@
+# Generated by Django 2.0.6 on 2018-10-29 14:36
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('payment', '0024_auto_20181002_0338'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='GiftCertificate',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('price', models.DecimalField(decimal_places=2, default=0, editable=False, max_digits=8)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='UserGiftCertificate',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('recipient', models.EmailField(max_length=254, blank=True, null=True,)),
+ ('bonuses_sent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='payment.UserBonus')),
+ ('gift_certificate', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='payment.GiftCertificate')),
+ ('payment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='payment.Payment')),
+ ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='gift_certificates', to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ ]
diff --git a/apps/payment/migrations/0026_auto_20181101_1546.py b/apps/payment/migrations/0026_auto_20181101_1546.py
new file mode 100644
index 00000000..45cd9906
--- /dev/null
+++ b/apps/payment/migrations/0026_auto_20181101_1546.py
@@ -0,0 +1,39 @@
+# Generated by Django 2.0.6 on 2018-11-01 15:46
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('payment', '0025_giftcertificate_usergiftcertificate'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='GiftCertificatePayment',
+ fields=[
+ ('payment_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='payment.Payment')),
+ ],
+ options={
+ 'verbose_name': 'Платеж за подарочный сертификат',
+ 'verbose_name_plural': 'Платежи за подарочные сертификаты',
+ },
+ bases=('payment.payment',),
+ ),
+ migrations.AlterModelOptions(
+ name='giftcertificate',
+ options={'ordering': ('price',)},
+ ),
+ migrations.AlterField(
+ model_name='giftcertificate',
+ name='price',
+ field=models.DecimalField(decimal_places=2, default=0, max_digits=8),
+ ),
+ migrations.AddField(
+ model_name='giftcertificatepayment',
+ name='gift_certificate',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to='payment.GiftCertificate', verbose_name='Подарочный сертификат'),
+ ),
+ ]
diff --git a/apps/payment/migrations/0027_auto_20181109_1402.py b/apps/payment/migrations/0027_auto_20181109_1402.py
new file mode 100644
index 00000000..a228e06b
--- /dev/null
+++ b/apps/payment/migrations/0027_auto_20181109_1402.py
@@ -0,0 +1,22 @@
+# Generated by Django 2.0.6 on 2018-11-09 14:02
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('payment', '0026_auto_20181101_1546'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='usergiftcertificate',
+ name='recipient',
+ ),
+ migrations.AddField(
+ model_name='giftcertificate',
+ name='cover',
+ field=models.CharField(blank=True, default='', max_length=255),
+ ),
+ ]
diff --git a/apps/payment/migrations/0028_add_gift_certificates.py b/apps/payment/migrations/0028_add_gift_certificates.py
new file mode 100644
index 00000000..18c0ac4f
--- /dev/null
+++ b/apps/payment/migrations/0028_add_gift_certificates.py
@@ -0,0 +1,21 @@
+# Generated by Django 2.0.6 on 2018-11-09 14:03
+
+from django.db import migrations
+from django.contrib.staticfiles.storage import staticfiles_storage
+
+
+def add_gift_certificates(apps, schema_editor):
+ GiftCertificate = apps.get_model('payment', 'GiftCertificate')
+ for price in [1000, 2000, 3000, 5000, 10000]:
+ GiftCertificate.objects.create(price=price,
+ cover=staticfiles_storage.url('img/gift-certificates/%d.jpg' % price))
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('payment', '0027_auto_20181109_1402'),
+ ]
+
+ operations = [
+ migrations.RunPython(add_gift_certificates),
+ ]
diff --git a/apps/payment/models.py b/apps/payment/models.py
index a953d5e8..413e8bf8 100644
--- a/apps/payment/models.py
+++ b/apps/payment/models.py
@@ -1,5 +1,6 @@
from decimal import Decimal
import arrow
+import short_url
from django.db.models import Func, F
from paymentwall import Pingback
@@ -13,6 +14,7 @@ from django.core.validators import RegexValidator
from django.utils.timezone import now
from django.conf import settings
+from apps.content.models import ImageObject
from project.utils import weekdays_in_date_range
from apps.course.models import Course
@@ -147,6 +149,8 @@ class Payment(PolymorphicModel):
referrer_bonus = user.referral.referrer_bonus
if payment and payment.is_paid():
price = payment.amount
+ elif isinstance(payment, GiftCertificatePayment):
+ price = payment.gift_certificate.price
elif course:
price = course.price
else:
@@ -244,6 +248,12 @@ class Payment(PolymorphicModel):
else:
author_balance.amount = self.amount
author_balance.save()
+ if isinstance(self, GiftCertificatePayment) and self.is_paid():
+ ugs, created = UserGiftCertificate.objects.get_or_create(user=self.user, gift_certificate=self.gift_certificate,
+ payment=self)
+ if created:
+ from apps.notification.tasks import send_gift_certificate
+ send_gift_certificate(ugs.id)
# Если юзер реферал и нет платежа, где применялась скидка
if hasattr(self.user, 'referral') and not self.user.referral.payment and self.is_paid():
# Платеж - как сигнал, что скидка применилась
@@ -283,6 +293,15 @@ class SchoolPayment(Payment):
return arrow.get(self.date_end, settings.TIME_ZONE).humanize(locale='ru')
+class GiftCertificatePayment(Payment):
+ gift_certificate = models.ForeignKey('GiftCertificate', on_delete=models.CASCADE,
+ verbose_name='Подарочный сертификат', related_name='payments')
+
+ class Meta:
+ verbose_name = 'Платеж за подарочный сертификат'
+ verbose_name_plural = 'Платежи за подарочные сертификаты'
+
+
class UserBonus(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='bonuses')
amount = models.DecimalField(max_digits=8, decimal_places=2, default=0, editable=False)
@@ -292,3 +311,22 @@ class UserBonus(models.Model):
class Meta:
ordering = ('created_at',)
+
+
+class GiftCertificate(models.Model):
+ price = models.DecimalField(max_digits=8, decimal_places=2, default=0)
+ cover = models.CharField(max_length=255, blank=True, default='')
+
+ class Meta:
+ ordering = ('price',)
+
+
+class UserGiftCertificate(models.Model):
+ gift_certificate = models.ForeignKey(GiftCertificate, on_delete=models.CASCADE,)
+ user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='gift_certificates')
+ payment = models.ForeignKey(Payment, on_delete=models.SET_NULL, null=True)
+ bonuses_sent = models.ForeignKey(UserBonus, on_delete=models.CASCADE, blank=True, null=True)
+
+ @property
+ def code(self):
+ return short_url.encode_url(self.id) if self.id else None
diff --git a/apps/payment/templates/payment/gift_certificate.html b/apps/payment/templates/payment/gift_certificate.html
new file mode 100644
index 00000000..7d291137
--- /dev/null
+++ b/apps/payment/templates/payment/gift_certificate.html
@@ -0,0 +1,17 @@
+{% extends "templates/lilcity/index.html" %} {% load static %}
+
+{% block content %}
+
+
+
Вам подарок!
+
+ Пользователь ХХХ дарит вам сертификат на сумму ХХХ руб
+
+
+
+
+{% endblock content %}
diff --git a/apps/payment/templates/payment/gift_certificate_get.html b/apps/payment/templates/payment/gift_certificate_get.html
new file mode 100644
index 00000000..6242f060
--- /dev/null
+++ b/apps/payment/templates/payment/gift_certificate_get.html
@@ -0,0 +1,15 @@
+{% extends "templates/lilcity/index.html" %} {% load static %} {% block content %}
+
+
+
+
Бонусы зачислены на ваш счет!
+
Вы можете оплатить с их помощью курс или онлайн-школу
+
+
+
+
+{% endblock content %}
diff --git a/apps/payment/templates/payment/gift_certificate_item.html b/apps/payment/templates/payment/gift_certificate_item.html
new file mode 100644
index 00000000..5f4a318e
--- /dev/null
+++ b/apps/payment/templates/payment/gift_certificate_item.html
@@ -0,0 +1,36 @@
+{% load thumbnail %}
+{% load static %}
+{% load data_liked from data_liked %}
+
+
+
+

+
+
+
+ {% if user_gift_certificate and not user_gift_certificate.bonuses_sent %}
+ подарочный сертификат
+ {% else %}подарочный сертификат{% endif %}
+
{{ gift_certificate.price|floatformat:"-2" }}₽
+
+ {% if user_gift_certificate %}
+ {% if user_gift_certificate.bonuses_sent %}
+
+

+ Получено
+
+ {% else %}
+
+

+ Ожидает получения
+
+ {% endif %}
+ {% else %}
+
Купить сертификат
+ {% endif %}
+
diff --git a/apps/payment/templates/payment/gift_certificate_items.html b/apps/payment/templates/payment/gift_certificate_items.html
new file mode 100644
index 00000000..05730083
--- /dev/null
+++ b/apps/payment/templates/payment/gift_certificate_items.html
@@ -0,0 +1,4 @@
+{% for gift_certificate in gift_certificates %}
+ {% cycle 'theme_pink2' 'theme_cyan' 'theme_violet2' as theme_color silent %}
+ {% include "payment/gift_certificate_item.html" %}
+{% endfor %}
diff --git a/apps/payment/templates/payment/gift_certificate_payment_success.html b/apps/payment/templates/payment/gift_certificate_payment_success.html
new file mode 100644
index 00000000..793eb25e
--- /dev/null
+++ b/apps/payment/templates/payment/gift_certificate_payment_success.html
@@ -0,0 +1,13 @@
+{% extends "templates/lilcity/index.html" %} {% load static %} {% block content %}
+
+
+
+
Вы успешно приобрели подарочный сертификат!
+
Мы отправили письмо с сертификатом на вашу почту.
+
+
+
+
+{% endblock content %}
diff --git a/apps/payment/templates/payment/gift_certificates.html b/apps/payment/templates/payment/gift_certificates.html
new file mode 100644
index 00000000..5fa4bf2e
--- /dev/null
+++ b/apps/payment/templates/payment/gift_certificates.html
@@ -0,0 +1,35 @@
+{% extends "templates/lilcity/index.html" %} {% load static %}
+
+{% block content %}
+
+
+
+ Подарочные сертификаты
+
+
+
+
+
+
+
Подарочный сертификат Lil City - отличный презент на любой праздник.
+А также сертификат можно подарить без повода - развитие
творческого потенциала всегда уместно и актуально.
+При покупке подарочного сертификата мы высылаем письмо,
+где будет уникальный код и уникальная ссылка. Письмо или ссылку
+на него вы можете переслать другому. Сертификатом можно
+воспользоваться, перейдя по ссылке.
+

+
+
Сертификаты
+
+ {% include "payment/gift_certificate_items.html" %}
+
+
+
+{% endblock content %}
diff --git a/apps/payment/templates/payment/payment_success.html b/apps/payment/templates/payment/payment_success.html
index 7d174018..9d256634 100644
--- a/apps/payment/templates/payment/payment_success.html
+++ b/apps/payment/templates/payment/payment_success.html
@@ -7,10 +7,17 @@
- {% else %}
+ {% endif %}
+ {% if course %}
Вы успешно приобрели курс!
+ {% endif %}
+ {% if gift_certificate %}
+
Вы успешно приобрели подарочный сертификат!
+
{% endif %}
diff --git a/apps/payment/views.py b/apps/payment/views.py
index 6be7734f..02a2b874 100644
--- a/apps/payment/views.py
+++ b/apps/payment/views.py
@@ -1,5 +1,5 @@
from decimal import Decimal
-
+import short_url
import arrow
import json
import logging
@@ -12,9 +12,9 @@ import calendar
from django.contrib import messages
from django.contrib.auth.decorators import login_required
-from django.http import HttpResponse
+from django.http import HttpResponse, Http404
from django.shortcuts import redirect, get_object_or_404
-from django.views.generic import View, TemplateView
+from django.views.generic import View, TemplateView, DetailView
from django.views.decorators.csrf import csrf_exempt
from django.urls import reverse_lazy
from django.utils.decorators import method_decorator
@@ -27,7 +27,8 @@ from apps.course.models import Course
from apps.school.models import SchoolSchedule
from apps.payment.tasks import transaction_to_mixpanel, product_payment_to_mixpanel, transaction_to_roistat
-from .models import AuthorBalance, CoursePayment, SchoolPayment, Payment, UserBonus
+from .models import AuthorBalance, CoursePayment, SchoolPayment, Payment, UserBonus, GiftCertificate, \
+ GiftCertificatePayment, UserGiftCertificate
logger = logging.getLogger('django')
@@ -216,6 +217,8 @@ class PaymentwallCallbackView(View):
product_payment_class = CoursePayment
elif product_type_name == 'school':
product_payment_class = SchoolPayment
+ elif product_type_name == 'gift_certificate':
+ product_payment_class = GiftCertificatePayment
else:
return HttpResponse(status=403)
@@ -264,6 +267,15 @@ class PaymentwallCallbackView(View):
'created_at': payment.created_at,
'update_at': payment.update_at,
}
+ elif product_type_name == 'gift_certificate':
+ properties = {
+ 'payment_id': payment.id,
+ 'amount': payment.amount,
+ 'status': payment.status,
+ 'gift_certificate': payment.gift_certificate.id,
+ 'created_at': payment.created_at,
+ 'update_at': payment.update_at,
+ }
payment.save()
product_payment_to_mixpanel.delay(
@@ -297,3 +309,84 @@ class PaymentwallCallbackView(View):
else:
raise DisallowedPingbackHost
return HttpResponse(status=403)
+
+
+class GiftCertificatesView(TemplateView):
+ model = GiftCertificate
+ template_name = 'payment/gift_certificates.html'
+
+ def get(self, request, *args, **kwargs):
+ gift_certificates = GiftCertificate.objects.all()
+ context = self.get_context_data(**kwargs)
+ context['gift_certificates'] = gift_certificates
+ return self.render_to_response(context)
+
+
+@method_decorator(login_required, name='dispatch')
+class GiftCertificateBuyView(TemplateView):
+ model = GiftCertificate
+ template_name = 'payment/paymentwall_widget.html'
+
+ def get(self, request, pk, *args, **kwargs):
+ gift_certificate = get_object_or_404(GiftCertificate, pk=pk)
+ roistat_visit = request.COOKIES.get('roistat_visit', None)
+ gift_certificate_payment = GiftCertificatePayment.objects.create(
+ user=request.user,
+ gift_certificate=gift_certificate,
+ roistat_visit=roistat_visit,)
+ context = self.get_context_data(**kwargs)
+ product = Product(
+ f'gift_certificate_{gift_certificate_payment.id}',
+ gift_certificate_payment.amount,
+ 'RUB',
+ 'Подарочный сертификат',
+ )
+ host = urlsplit(self.request.META.get('HTTP_REFERER'))
+ host = str(host[0]) + '://' + str(host[1])
+ widget = Widget(
+ str(request.user.id),
+ 'p1_1',
+ [product],
+ extra_params={
+ 'lang': 'ru',
+ 'evaluation': 1,
+ 'demo': 1,
+ 'test_mode': 1,
+ 'success_url': host + str(reverse_lazy('gift-certificate-payment-success', args=[gift_certificate_payment.id])),
+ 'failure_url': host + str(reverse_lazy('payment-error')),
+ }
+ )
+ context['widget'] = widget.get_html_code()
+ return self.render_to_response(context)
+
+
+@method_decorator(login_required, name='dispatch')
+class GiftCertificateBuySuccessView(TemplateView):
+ template_name = 'payment/gift_certificate_payment_success.html'
+
+ def get(self, request, payment_id=None, *args, **kwargs):
+ try:
+ GiftCertificatePayment.objects.get(id=payment_id)
+ except:
+ raise Http404()
+ return self.render_to_response(context={'gift_certificate': True})
+
+
+@method_decorator(login_required, name='dispatch')
+class GiftCertificateGetView(TemplateView):
+ template_name = 'payment/gift_certificate_get.html'
+
+ def get(self, request, slug, *args, **kwargs):
+ try:
+ ugs = get_object_or_404(UserGiftCertificate, pk=short_url.decode_url(slug))
+ except:
+ raise Http404()
+ if UserBonus.objects.filter(payment=ugs.payment).exists():
+ raise Http404()
+ bonuses = UserBonus.objects.create(user=request.user, amount=ugs.gift_certificate.price,
+ payment=ugs.payment)
+ ugs.bonuses_sent = bonuses
+ ugs.save()
+ context = self.get_context_data(**kwargs)
+ return self.render_to_response(context)
+
diff --git a/apps/school/templates/summer/promo.html b/apps/school/templates/summer/promo.html
index 8b1ed12e..7d12fac4 100644
--- a/apps/school/templates/summer/promo.html
+++ b/apps/school/templates/summer/promo.html
@@ -20,6 +20,9 @@
{% if is_purchased %}ваша подписка истекает {{ subscription_ends_humanize }}