diff --git a/apps/course/models.py b/apps/course/models.py index ad520377..d17083b6 100644 --- a/apps/course/models.py +++ b/apps/course/models.py @@ -144,6 +144,11 @@ class Course(BaseModel, DeactivatedMixin): return super().save() + @property + def age_str(self): + ages = dict(self.AGE_CHOICES) + return ages.get(self.age) + @property def url(self): return self.get_absolute_url() diff --git a/apps/course/templates/course/course.html b/apps/course/templates/course/course.html index db228e96..3dd7eba8 100644 --- a/apps/course/templates/course/course.html +++ b/apps/course/templates/course/course.html @@ -394,5 +394,18 @@ {% block foot %} {% include "templates/blocks/popup_course_buy.html" %} - {% endblock foot %} + +{% block foot_js %} + + +{% endblock foot_js %} diff --git a/apps/course/templatetags/lil_utils.py b/apps/course/templatetags/lil_utils.py new file mode 100644 index 00000000..feb6ac93 --- /dev/null +++ b/apps/course/templatetags/lil_utils.py @@ -0,0 +1,12 @@ +from django import template + +register = template.Library() + + +@register.simple_tag(takes_context=True) +def same_url(context, **kwargs): + request = context.get('request') + args = request.GET.copy() + args.update(kwargs) + return '%s?%s' % (request.path, args.urlencode()) + diff --git a/apps/payment/models.py b/apps/payment/models.py index 47af6711..ef345f45 100644 --- a/apps/payment/models.py +++ b/apps/payment/models.py @@ -301,7 +301,7 @@ class Payment(PolymorphicModel): payment=self) if created: from apps.notification.tasks import send_gift_certificate - send_gift_certificate(ugs.id) + send_gift_certificate.delay(ugs.id) # Если это не первая покупка, - отправляем бонусы юзеру if self.user.paid_one_more and not self.user.bonuses.filter( is_service=True, action_name=UserBonus.ACTION_PAID_ONE_MORE).count(): diff --git a/apps/payment/templates/payment/course_payment_success.html b/apps/payment/templates/payment/course_payment_success.html deleted file mode 100644 index 04139183..00000000 --- a/apps/payment/templates/payment/course_payment_success.html +++ /dev/null @@ -1,12 +0,0 @@ -{% 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 index 7d4d3d8f..3ee2a6f5 100644 --- a/apps/payment/templates/payment/gift_certificate_item.html +++ b/apps/payment/templates/payment/gift_certificate_item.html @@ -28,6 +28,7 @@ Купить сертификат diff --git a/apps/payment/templates/payment/gift_certificate_payment_success.html b/apps/payment/templates/payment/gift_certificate_payment_success.html deleted file mode 100644 index 793eb25e..00000000 --- a/apps/payment/templates/payment/gift_certificate_payment_success.html +++ /dev/null @@ -1,13 +0,0 @@ -{% extends "templates/lilcity/index.html" %} {% load static %} {% block content %} -
-
-
-
Вы успешно приобрели подарочный сертификат!
-
Мы отправили письмо с сертификатом на вашу почту.
- -
-
-
-{% endblock content %} diff --git a/apps/payment/templates/payment/pay.html b/apps/payment/templates/payment/pay.html index ec26e9ca..c381526b 100644 --- a/apps/payment/templates/payment/pay.html +++ b/apps/payment/templates/payment/pay.html @@ -1,21 +1,24 @@ {% extends "templates/lilcity/index.html" %} {% load static %} {% load rupluralize from plural %} +{% load same_url from lil_utils %} {% block content %}
- {% if school and request.user.bonus and not payment.bonus %} - {% if request.user.bonus >= payment.amount %} - Оплатить бонусами -

или купить онлайн

- {% else %} -

Для оплаты части стоимости вы можете использовать бонусы

- - {% endif %} + {% if school or gift_certificate %} + {% if request.user.bonus and not payment.bonus %} + {% if request.user.bonus >= payment.amount %} + Оплатить бонусами +

или купить онлайн

+ {% else %} +

Для оплаты части стоимости вы можете использовать бонусы

+ + {% endif %} + {% endif %} {% endif %}
{% include "./paymentwall_widget.html" %} @@ -24,6 +27,45 @@
{% endblock content %} -{% block foot %} +{% block foot_js %} + -{% endblock foot %} +{% endblock foot_js %} diff --git a/apps/payment/templates/payment/payment_success.html b/apps/payment/templates/payment/payment_success.html index c9b8d6fb..e5187e5d 100644 --- a/apps/payment/templates/payment/payment_success.html +++ b/apps/payment/templates/payment/payment_success.html @@ -1,8 +1,17 @@ -{% extends "templates/lilcity/index.html" %} {% load static %}{% load plural %} +{% extends "templates/lilcity/index.html" %} +{% load static %} +{% load rupluralize from plural %} + {% block content %}
+ {% if course %} +
Вы успешно приобрели курс!
+ + {% endif %} {% if school %}
Вы успешно приобрели доступ на {{ duration|rupluralize:"месяц,месяца,месяцев" }}!
@@ -15,19 +24,62 @@ ПЕРЕЙТИ В ЛАГЕРЬ
{% endif %} - {% if course %} -
Вы успешно приобрели курс!
- - {% endif %} {% if gift_certificate %}
Вы успешно приобрели подарочный сертификат!
+
Мы отправили письмо с сертификатом на вашу почту.
{% endif %}
{% endblock content %} + +{% block foot_js %} + +{% endblock foot_js %} diff --git a/apps/payment/views.py b/apps/payment/views.py index 3c6547b3..44c5506c 100644 --- a/apps/payment/views.py +++ b/apps/payment/views.py @@ -4,7 +4,7 @@ import json import logging from datetime import timedelta -from urllib.parse import urlsplit +from urllib.parse import urlsplit, urlencode import datetime @@ -37,22 +37,28 @@ class DisallowedPingbackHost(Exception): @method_decorator(login_required, name='dispatch') class CourseBuySuccessView(TemplateView): - template_name = 'payment/course_payment_success.html' + template_name = 'payment/payment_success.html' - def get(self, request, pk=None, *args, **kwargs): - course = get_object_or_404(Course, pk=pk) - return self.render_to_response(context={'course': course}) + def get(self, request, payment_id, *args, **kwargs): + payment = CoursePayment.objects.get(pk=payment_id) + course = payment.course + return self.render_to_response(context={'course': course, 'payment': payment}) @method_decorator(login_required, name='dispatch') class SchoolBuySuccessView(TemplateView): template_name = 'payment/payment_success.html' - def get(self, request, pk=None, is_camp=False, *args, **kwargs): + def get(self, request, payment_id, is_camp=False, *args, **kwargs): + if is_camp: + payment = DrawingCampPayment.objects.get(pk=payment_id) + else: + payment = SchoolPayment.objects.get(pk=payment_id) context = { 'duration': request.GET.get('duration'), 'camp': is_camp, - 'school': not is_camp + 'school': not is_camp, + 'payment': payment, } return self.render_to_response(context=context) @@ -93,7 +99,7 @@ class CourseBuyView(TemplateView): course_payment.bonus = bonus course_payment.save() if course_payment.is_paid(): - return redirect(reverse_lazy('course_payment_success', args=[course.id])) + return redirect(reverse_lazy('course_payment_success', args=[course_payment.id])) product = Product( f'course_{course_payment.id}', course_payment.amount, @@ -110,7 +116,7 @@ class CourseBuyView(TemplateView): 'evaluation': 1, 'demo': 1, 'test_mode': 1, - 'success_url': host + str(reverse_lazy('course_payment_success', args=[course.id])), + 'success_url': host + str(reverse_lazy('course_payment_success', args=[course_payment.id])), 'failure_url': host + str(reverse_lazy('payment-error')), } ) @@ -118,7 +124,11 @@ class CourseBuyView(TemplateView): if request.user_agent.is_mobile: attrs['width'] = '100%' attrs['height'] = '600' - return self.render_to_response(context={'widget': widget.get_html_code(attrs)}) + return self.render_to_response(context={ + 'widget': widget.get_html_code(attrs), + 'payment': course_payment, + 'course': course, + }) @method_decorator(login_required, name='dispatch') @@ -164,7 +174,7 @@ class SchoolBuyView(TemplateView): school_payment.bonus = bonus school_payment.save() if school_payment.is_paid(): - return redirect('%s?duration=%s' % (str(reverse_lazy('payment-success')), duration)) + return redirect('%s?duration=%s' % (str(reverse_lazy('payment-success', args=[school_payment.id])), duration)) if payment_id and school_payment.bonus and not use_bonuses: bonus = school_payment.bonus school_payment.amount += school_payment.bonus @@ -186,7 +196,7 @@ class SchoolBuyView(TemplateView): 'evaluation': 1, 'demo': 1, 'test_mode': 1, - 'success_url': host + str(reverse_lazy('payment-success')) + '?duration=%s' % duration, + 'success_url': host + str(reverse_lazy('payment-success', args=[school_payment.id])) + '?duration=%s' % duration, 'failure_url': host + str(reverse_lazy('payment-error')), } ) @@ -246,7 +256,7 @@ class DrawingCampBuyView(TemplateView): camp_payment.bonus = bonus camp_payment.save() if camp_payment.is_paid(): - return redirect(reverse_lazy('camp-payment-success')) + return redirect(reverse_lazy('camp-payment-success', args=[camp_payment.id])) product = Product( f'drawing_camp_{camp_payment.id}', camp_payment.amount, @@ -263,7 +273,7 @@ class DrawingCampBuyView(TemplateView): 'evaluation': 1, 'demo': 1, 'test_mode': 1, - 'success_url': host + str(reverse_lazy('camp-payment-success')), + 'success_url': host + str(reverse_lazy('camp-payment-success', args=[camp_payment.id])), 'failure_url': host + str(reverse_lazy('payment-error')), } ) @@ -271,7 +281,11 @@ class DrawingCampBuyView(TemplateView): if request.user_agent.is_mobile: attrs['width'] = '100%' attrs['height'] = '600' - return self.render_to_response(context={'widget': widget.get_html_code(attrs)}) + return self.render_to_response(context={ + 'widget': widget.get_html_code(attrs), + 'payment': camp_payment, + 'camp': True, + }) @method_decorator(csrf_exempt, name='dispatch') @@ -432,10 +446,25 @@ class GiftCertificateBuyView(TemplateView): def get(self, request, pk, *args, **kwargs): gift_certificate = get_object_or_404(GiftCertificate, pk=pk) roistat_visit = request.COOKIES.get('roistat_visit', None) + use_bonuses = request.GET.get('use_bonuses') gift_certificate_payment = GiftCertificatePayment.objects.create( user=request.user, gift_certificate=gift_certificate, roistat_visit=roistat_visit,) + if use_bonuses and request.user.bonus: + if request.user.bonus >= gift_certificate_payment.amount: + bonus = UserBonus.objects.create(amount=-gift_certificate_payment.amount, user=request.user, + payment=gift_certificate_payment) + gift_certificate_payment.amount = 0 + gift_certificate_payment.status = Pingback.PINGBACK_TYPE_REGULAR + else: + bonus = UserBonus.objects.create(amount=-request.user.bonus, user=request.user, + payment=gift_certificate_payment) + gift_certificate_payment.amount -= request.user.bonus + gift_certificate_payment.bonus = bonus + gift_certificate_payment.save() + if gift_certificate_payment.is_paid(): + return redirect(reverse_lazy('gift-certificate-payment-success', args=[gift_certificate_payment.id])) context = self.get_context_data(**kwargs) product = Product( f'gift_certificate_{gift_certificate_payment.id}', @@ -463,19 +492,24 @@ class GiftCertificateBuyView(TemplateView): attrs['width'] = '100%' attrs['height'] = '600' context['widget'] = widget.get_html_code(attrs) + context['payment'] = gift_certificate_payment + context['gift_certificate'] = gift_certificate return self.render_to_response(context) @method_decorator(login_required, name='dispatch') class GiftCertificateBuySuccessView(TemplateView): - template_name = 'payment/gift_certificate_payment_success.html' + template_name = 'payment/payment_success.html' - def get(self, request, payment_id=None, *args, **kwargs): + def get(self, request, payment_id, *args, **kwargs): try: - GiftCertificatePayment.objects.get(id=payment_id) + payment = GiftCertificatePayment.objects.get(id=payment_id) except: raise Http404() - return self.render_to_response(context={'gift_certificate': True}) + return self.render_to_response(context={ + 'gift_certificate': payment.gift_certificate, + 'payment': payment, + }) @method_decorator(login_required, name='dispatch') diff --git a/project/templates/blocks/lil_store_js.html b/project/templates/blocks/lil_store_js.html index 54c9ca60..8bd16825 100644 --- a/project/templates/blocks/lil_store_js.html +++ b/project/templates/blocks/lil_store_js.html @@ -22,8 +22,11 @@ }, components: {}, urls: { + course: /course\/\w+\/?$/, + courseBuy: /course\/\d+\/checkout/, + courseBuySuccess: /payments\/course\/\d+\/success/, courses: "{% url 'courses' %}", - courseEdit: /\/course\/\d+\/edit/, + courseEdit: /course\/\d+\/edit/, courseCreate: "{% url 'course_create' %}", userProfileEdit: "{% url 'user-edit-profile' %}", userProfile: "{% url 'user-profile' %}", @@ -31,6 +34,16 @@ faq: "{% url 'faq' %}", contestEdit: /contest\/\w+\/edit/, userGalleryEdit: "{% url 'user-gallery-edit' %}", + school: "{% url 'school:school' %}", + schoolBuy: "{% url 'school-checkout' %}", + schoolBuySuccess: /payments\/school\/\d+\/success/, + camp: "{% url 'school:drawing-camp' %}", + campBuy: "{% url 'camp-checkout' %}", + campBuySuccess: /payments\/camp\/\d+\/success/, + giftCertificates: "{% url 'gift-certificates' %}", + giftCertificateBuy: /gift-certificate\/\d+\/checkout/, + giftCertificateBuySuccess: /payments\/gift-certificate\/\d+\/success/, + prices: "{% url 'packages' %}", }, flags: { referrer: '{{ referrer.id|default:'' }}', @@ -44,7 +57,7 @@ urlPatternNames = [urlPatternNames]; } return urlPatternNames.filter(function(urlPatternName){ - return window.location.pathname.search(window.LIL_STORE.urls[urlPatternName]) > -1; + return window.LIL_STORE.urls[urlPatternName] && window.location.pathname.search(window.LIL_STORE.urls[urlPatternName]) > -1; }).length > 0; }, isIndexPage: window.location.pathname == '/', diff --git a/project/templates/lilcity/layer.html b/project/templates/lilcity/layer.html index 50245a19..593a632b 100644 --- a/project/templates/lilcity/layer.html +++ b/project/templates/lilcity/layer.html @@ -68,8 +68,6 @@ {% block layer_head %}{% endblock layer_head %} - {% block layer_body %} - {% endblock layer_body %} {% if settings.DEV_SERVER or settings.DEBUG %} {% endif %} + + {% block layer_body %} + {% endblock layer_body %} diff --git a/project/templates/lilcity/packages.html b/project/templates/lilcity/packages.html index 11e8ebd2..e6dda76e 100644 --- a/project/templates/lilcity/packages.html +++ b/project/templates/lilcity/packages.html @@ -38,6 +38,7 @@ {% if user.is_authenticated %} href="{% url 'school-checkout' %}?duration={{ package.duration }}" + data-package="{{ package.id }}" data-duration="{{ package.duration }}" data-price="{{ package.price|floatformat:'0' }}" {% else %} href="#" data-popup=".js-popup-auth" {% endif %} diff --git a/project/urls.py b/project/urls.py index 1f8ff1dc..3d6ce49c 100644 --- a/project/urls.py +++ b/project/urls.py @@ -66,9 +66,9 @@ urlpatterns = [ path('lesson//comment', lessoncomment, name='lessoncomment'), path('payments/ping', PaymentwallCallbackView.as_view(), name='payment-ping'), path('paymentwall/pingback', PaymentwallCallbackView.as_view(), name='payment-ping-second'), - path('payments/course//success', CourseBuySuccessView.as_view(), name='course_payment_success'), - path('payments/school/success', SchoolBuySuccessView.as_view(), name='payment-success'), - path('payments/school/camp/success', SchoolBuySuccessView.as_view(), name='camp-payment-success', + path('payments/course//success', CourseBuySuccessView.as_view(), name='course_payment_success'), + path('payments/school//success', SchoolBuySuccessView.as_view(), name='payment-success'), + path('payments/school/camp//success', SchoolBuySuccessView.as_view(), name='camp-payment-success', kwargs={'is_camp': True}), path('payments/error', TemplateView.as_view(template_name='payment/payment_error.html'), name='payment-error'), path('school/checkout', SchoolBuyView.as_view(), name='school-checkout'), diff --git a/web/package.json b/web/package.json index 5101212a..61af19be 100755 --- a/web/package.json +++ b/web/package.json @@ -10,7 +10,7 @@ "devDependencies": { "autoprefixer": "^6.3.3", "babel-core": "^6.26.0", - "babel-loader": "^7.1.2", + "babel-loader": "^7.1.5", "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-env": "^1.6.1", "babel-preset-es2015": "^6.24.1", @@ -24,13 +24,13 @@ "node-sass": "^4.12.0", "require-dir": "^0.3.0", "run-sequence": "^1.1.5", - "sass-loader": "^7.0.1", + "sass-loader": "^7.3.1", "style-loader": "^0.20.1", - "through2": "^2.0.1", + "through2": "^2.0.5", "url-loader": "^0.6.2", - "vue-loader": "^14.1.1", + "vue-loader": "^14.2.4", "vue-style-loader": "^3.1.2", - "vue-template-compiler": "^2.5.13", + "vue-template-compiler": "^2.6.10", "webpack": "^3.10.0" }, "dependencies": { @@ -38,32 +38,32 @@ "autosize-input": "^1.0.2", "axios": "^0.19.0", "babel-polyfill": "^6.26.0", - "baguettebox.js": "^1.10.0", - "bowser": "^2.1.2", - "clipboard": "^2.0.1", - "downscale": "^1.0.4", + "baguettebox.js": "^1.11.0", + "bowser": "^2.6.0", + "clipboard": "^2.0.4", + "downscale": "^1.0.6", "extract-loader": "^3.1.0", - "glob": "^7.1.2", - "history": "^4.7.2", + "glob": "^7.1.4", + "history": "^4.9.0", "ilyabirman-likely": "^2.3.0", "inputmask": "^3.3.11", "jquery": "^3.4.1", - "js-cookie": "^2.2.0", + "js-cookie": "^2.2.1", "lodash.debounce": "^4.0.8", "modal-video": "git+https://github.com/gzbender/modal-video.git", - "moment": "^2.20.1", + "moment": "^2.24.0", "owl.carousel": "^2.2.0", - "slugify": "^1.2.9", + "slugify": "^1.3.5", "smooth-scroll": "^12.1.5", - "sortablejs": "^1.7.0", - "svg-sprite-loader": "^3.7.3", - "uuid": "^3.2.1", + "sortablejs": "^1.9.0", + "svg-sprite-loader": "^3.9.2", + "uuid": "^3.3.3", "validator": "^9.2.0", - "vue": "^2.5.13", + "vue": "^2.6.10", "vue-autosize": "^1.0.2", "vue-awesome-swiper": "^3.1.3", "vue-tags-component": "^1.3.0", - "vuedraggable": "^2.16.0", + "vuedraggable": "^2.23.0", "vuejs-datepicker": "^0.9.25", "vuelidate": "^0.6.1" } diff --git a/web/src/js/app.js b/web/src/js/app.js index a59403d6..bdf00d96 100644 --- a/web/src/js/app.js +++ b/web/src/js/app.js @@ -17,6 +17,7 @@ import "./modules/comments"; import "./modules/password-show"; import "./modules/notification"; import "./modules/mixpanel"; +import "./modules/gtag"; import "../sass/app.sass"; diff --git a/web/src/js/modules/gtag.js b/web/src/js/modules/gtag.js new file mode 100644 index 00000000..135a0707 --- /dev/null +++ b/web/src/js/modules/gtag.js @@ -0,0 +1,96 @@ +import $ from 'jquery'; + +$(document).ready(function () { + if(! window.LIL_STORE.user.id){ + return; + } + + if(window.LIL_STORE.urlIs(['courseBuy', 'schoolBuy', 'campBuy', 'giftCertificateBuy'])){ + if(window.LIL_STORE.urlIs(['schoolBuy', 'giftCertificateBuy'])){ + const $useBonuses = $('#use-bonuses-checkbox'); + $useBonuses.find('.switch__content').click(() => { + window.gtag('event', 'set_checkout_option', { + "checkout_step": 1, + "checkout_option": "use_bonuses", + "value": +!$useBonuses.find('input').prop('checked'), + }); + }); + $('#pay-with-bonuses').click(() => { + window.gtag('event', 'set_checkout_option', { + "checkout_step": 1, + "checkout_option": "use_bonuses", + "value": 1, + }); + }) + } + window.gtag('event', 'checkout_progress', { + "items": [window.LIL_STORE.data.gtagProduct], + }); + } + + if(window.LIL_STORE.urlIs(['courseBuySuccess', 'schoolBuySuccess', 'campBuySuccess', 'giftCertificateBuySuccess'])){ + window.gtag('event', 'checkout_progress', { + "items": [window.LIL_STORE.data.gtagProduct], + }); + window.gtag('event', 'purchase', window.LIL_STORE.data.gtagTransaction); + } + + if(window.LIL_STORE.urlIs('course')){ + const $btn = $('[data-popup=".js-popup-course-buy"]'); + $btn.click(() => { + window.gtag('event', 'begin_checkout', { + "items": [window.LIL_STORE.data.gtagProduct], + }); + }); + const $popup = $('.js-popup-course-buy'); + const $useBonusesSwitch = $popup.find('.buy__bonuses .switch__content'); + const $useBonusesInput = $popup.find('.buy__bonuses input'); + $useBonusesSwitch.click(() => { + window.gtag('event', 'set_checkout_option', { + "checkout_step": 1, + "checkout_option": "use_bonuses", + "value": +!$useBonusesInput.prop('checked'), + }); + }); + const $buyBtn = $popup.find('.buy__btn'); + $buyBtn.click(() => { + window.gtag('event', 'checkout_progress', { + "items": [window.LIL_STORE.data.gtagProduct], + }); + }); + } + + if(window.LIL_STORE.urlIs('prices')){ + const $btns = $('.package__btn'); + $btns.click(function () { + const $this = $(this); + window.gtag('event', 'begin_checkout', { + "items": [{ + id: $this.data('package'), + name: 'Подписка на ' + +$this.data('duration') + ' месяцев', + category: 'Подписка', + brand: 'Lil.School', + quantity: 1, + price: +$this.data('price'), + }], + }); + }) + } + + if(window.LIL_STORE.urlIs('giftCertificates')){ + const $btns = $('.gift-certificates__buy-btn'); + $btns.click(function () { + const $this = $(this); + window.gtag('event', 'begin_checkout', { + "items": [{ + id: $this.data('gift-certificate'), + name: 'Подарочный сертификат на ' + +$this.data('price') + ' руб', + category: 'Подарочный сертификат', + brand: 'Lil.School', + quantity: 1, + price: +$this.data('price'), + }], + }); + }); + } +});