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

remotes/origin/hotfix/LIL-691
gzbender 7 years ago
commit 9e055651e3
  1. 4
      api/v1/serializers/config.py
  2. 26
      apps/auth/views.py
  3. 23
      apps/config/migrations/0009_auto_20180729_0503.py
  4. 23
      apps/config/migrations/0010_auto_20180820_0853.py
  5. 4
      apps/config/models.py
  6. 14
      apps/course/migrations/0045_merge_20180926_0200.py
  7. 4
      apps/course/templates/course/_items.html
  8. 4
      apps/course/templates/course/course.html
  9. 2
      apps/course/templates/course/course_only_lessons.html
  10. 2
      apps/course/templates/course/lesson.html
  11. 23
      apps/payment/migrations/0020_userbonus.py
  12. 27
      apps/payment/migrations/0021_auto_20180903_2257.py
  13. 19
      apps/payment/migrations/0022_auto_20180904_0106.py
  14. 19
      apps/payment/migrations/0023_payment_bonus.py
  15. 100
      apps/payment/models.py
  16. 22
      apps/payment/views.py
  17. 41
      apps/school/views.py
  18. 1
      apps/user/forms.py
  19. 31
      apps/user/migrations/0024_referral.py
  20. 18
      apps/user/migrations/0024_user_slug.py
  21. 14
      apps/user/migrations/0025_merge_20180927_2353.py
  22. 35
      apps/user/models.py
  23. 14
      apps/user/templates/blocks/profile-menu.html
  24. 71
      apps/user/templates/user/bonus-history.html
  25. 10
      apps/user/templates/user/notification-settings.html
  26. 36
      apps/user/templates/user/payment-history.html
  27. 46
      apps/user/templates/user/profile-settings.html
  28. 27
      apps/user/views.py
  29. 19
      project/context_processors.py
  30. 3
      project/settings.py
  31. 8
      project/templates/blocks/lil_store_js.html
  32. 16
      project/templates/blocks/popup_auth.html
  33. 13
      project/templates/blocks/popup_buy.html
  34. 2
      project/templates/blocks/teachers.html
  35. 36
      project/templates/blocks/user_menu.html
  36. 2
      project/templates/lilcity/index.html
  37. 8
      project/urls.py
  38. 15
      project/views.py
  39. 1
      requirements.txt
  40. 12
      web/src/js/app.js
  41. 4
      web/src/js/modules/auth.js
  42. 23
      web/src/js/modules/common.js
  43. 42
      web/src/js/modules/popup.js
  44. 58
      web/src/js/modules/profile.js
  45. 5
      web/src/js/utils.js
  46. 64
      web/src/sass/_common.sass

@ -20,6 +20,8 @@ class ConfigSerializer(serializers.ModelSerializer):
MAIN_PAGE_TOP_IMAGE = serializers.SerializerMethodField() MAIN_PAGE_TOP_IMAGE = serializers.SerializerMethodField()
# SCHOOL_LOGO_IMAGE = serializers.ImageField(required=False, allow_null=True) # SCHOOL_LOGO_IMAGE = serializers.ImageField(required=False, allow_null=True)
# MAIN_PAGE_TOP_IMAGE = serializers.ImageField(required=False, allow_null=True) # MAIN_PAGE_TOP_IMAGE = serializers.ImageField(required=False, allow_null=True)
REFERRER_BONUS = serializers.IntegerField()
REFERRAL_BONUS = serializers.IntegerField()
class Meta: class Meta:
model = Config model = Config
@ -37,6 +39,8 @@ class ConfigSerializer(serializers.ModelSerializer):
'INSTAGRAM_PROFILE_URL', 'INSTAGRAM_PROFILE_URL',
'SCHOOL_LOGO_IMAGE', 'SCHOOL_LOGO_IMAGE',
'MAIN_PAGE_TOP_IMAGE', 'MAIN_PAGE_TOP_IMAGE',
'REFERRER_BONUS',
'REFERRAL_BONUS',
) )
def get_SCHOOL_LOGO_IMAGE(self, config): def get_SCHOOL_LOGO_IMAGE(self, config):

@ -19,6 +19,7 @@ from django.shortcuts import redirect
from apps.notification.utils import send_email from apps.notification.utils import send_email
from apps.config.models import Config from apps.config.models import Config
from apps.user.models import Referral
from .forms import LearnerRegistrationForm from .forms import LearnerRegistrationForm
from .tokens import verification_email_token from .tokens import verification_email_token
@ -31,6 +32,7 @@ class LearnerRegistrationView(FormView):
template_name = "auth/registration-learner.html" template_name = "auth/registration-learner.html"
def form_valid(self, form): def form_valid(self, form):
config = Config.load()
first_name = form.cleaned_data['first_name'] first_name = form.cleaned_data['first_name']
last_name = form.cleaned_data['last_name'] last_name = form.cleaned_data['last_name']
email = form.cleaned_data['email'].lower() email = form.cleaned_data['email'].lower()
@ -52,15 +54,20 @@ class LearnerRegistrationView(FormView):
user.set_password(password) user.set_password(password)
user.save() user.save()
referrer = self.request.session.get('referrer')
if referrer:
Referral.objects.create(referral=user, referrer_id=referrer, bonus=config.REFERRAL_BONUS,
referrer_bonus=config.REFERRER_BONUS)
# TODO: email admins? мб реферера уже нет, старая ссылка
self.request.session['referrer'] = None
login(self.request, user) login(self.request, user)
# fixme: change email text # fixme: change email text
# fixme: async send email # fixme: async send email
config = Config.load() http_referer = urlsplit(self.request.META.get('HTTP_REFERER'))
refferer = urlsplit(self.request.META.get('HTTP_REFERER')) http_referer = str(http_referer[0]) + '://' + str(http_referer[1])
refferer = str(refferer[0]) + '://' + str(refferer[1])
token = verification_email_token.make_token(user) token = verification_email_token.make_token(user)
url = refferer + str(reverse_lazy('lilcity:verification-email', args=[token, user.id])) url = http_referer + str(reverse_lazy('lilcity:verification-email', args=[token, user.id]))
send_email('Вы успешно прошли регистрацию', email, "notification/email/verification_email.html", url=url, config=config) send_email('Вы успешно прошли регистрацию', email, "notification/email/verification_email.html", url=url, config=config)
return JsonResponse({"success": True}, status=201) return JsonResponse({"success": True}, status=201)
@ -81,6 +88,7 @@ class LoginView(FormView):
def form_valid(self, form): def form_valid(self, form):
login(self.request, form.get_user()) login(self.request, form.get_user())
self.request.session['referrer'] = None
return JsonResponse({"success": True}) return JsonResponse({"success": True})
def form_invalid(self, form): def form_invalid(self, form):
@ -97,6 +105,7 @@ class VerificationEmailView(View):
user.is_email_proved = True user.is_email_proved = True
user.save() user.save()
login(request, user) login(request, user)
self.request.session['referrer'] = None
return redirect(reverse_lazy('lilcity:success-verification-email')) return redirect(reverse_lazy('lilcity:success-verification-email'))
else: else:
return JsonResponse({"success": False}, status=400) return JsonResponse({"success": False}, status=400)
@ -186,6 +195,14 @@ class FacebookLoginOrRegistration(View):
user.photo.save(fname, photo, save=True) user.photo.save(fname, photo, save=True)
user.save() user.save()
referrer = self.request.session.get('referrer')
if referrer:
config = Config.load()
Referral.objects.create(referral=user, referrer_id=referrer, bonus=config.REFERRAL_BONUS,
referrer_bonus=config.REFERRER_BONUS)
# TODO: email admins? мб реферера уже нет, старая ссылка
self.request.session['referrer'] = None
login(requests, user=user) login(requests, user=user)
return JsonResponse({"success": True}) return JsonResponse({"success": True})
else: else:
@ -194,4 +211,5 @@ class FacebookLoginOrRegistration(View):
fname = str(fb_id) + '.jpg' fname = str(fb_id) + '.jpg'
user.photo.save(fname, photo, save=True) user.photo.save(fname, photo, save=True)
login(requests, user=user) login(requests, user=user)
self.request.session['referrer'] = None
return JsonResponse({"success": True}) return JsonResponse({"success": True})

@ -0,0 +1,23 @@
# Generated by Django 2.0.6 on 2018-07-29 05:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('config', '0008_auto_20180425_1451'),
]
operations = [
migrations.AddField(
model_name='config',
name='REFERRAL_DISCOUNT',
field=models.IntegerField(default=10),
),
migrations.AddField(
model_name='config',
name='REFERRER_CASHBACK',
field=models.IntegerField(default=10),
),
]

@ -0,0 +1,23 @@
# Generated by Django 2.0.6 on 2018-08-20 08:53
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('config', '0009_auto_20180729_0503'),
]
operations = [
migrations.RenameField(
model_name='config',
old_name='REFERRAL_DISCOUNT',
new_name='REFERRAL_BONUS',
),
migrations.RenameField(
model_name='config',
old_name='REFERRER_CASHBACK',
new_name='REFERRER_BONUS',
),
]

@ -17,6 +17,8 @@ class Config(models.Model):
SERVICE_DISCOUNT = models.IntegerField(default=1000) SERVICE_DISCOUNT = models.IntegerField(default=1000)
SCHOOL_LOGO_IMAGE = models.ImageField(null=True, blank=True) SCHOOL_LOGO_IMAGE = models.ImageField(null=True, blank=True)
MAIN_PAGE_TOP_IMAGE = models.ImageField(null=True, blank=True) MAIN_PAGE_TOP_IMAGE = models.ImageField(null=True, blank=True)
REFERRER_BONUS = models.IntegerField(default=10)
REFERRAL_BONUS = models.IntegerField(default=10)
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
self.pk = 1 self.pk = 1
@ -45,5 +47,7 @@ class Config(models.Model):
'SERVICE_DISCOUNT': '', 'SERVICE_DISCOUNT': '',
'SCHOOL_LOGO_IMAGE': '', 'SCHOOL_LOGO_IMAGE': '',
'MAIN_PAGE_TOP_IMAGE': '', 'MAIN_PAGE_TOP_IMAGE': '',
'REFERRER_BONUS': '',
'REFERRAL_BONUS': '',
} }
return obj return obj

@ -0,0 +1,14 @@
# Generated by Django 2.0.6 on 2018-09-26 02:00
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('course', '0044_course_age'),
('course', '0044_livelessoncomment'),
]
operations = [
]

@ -57,7 +57,7 @@
<div class="courses__content">{{ course.short_description | safe | linebreaks | truncatechars_html:300 }} <div class="courses__content">{{ course.short_description | safe | linebreaks | truncatechars_html:300 }}
</div> </div>
<div class="courses__user user"> <div class="courses__user user">
<a href="{% if course.author %}{% url 'user' course.author.id %}{% endif %}"> <a href="{% if course.author %}{{ course.author.url }}{% endif %}">
{% if course.author.photo %} {% if course.author.photo %}
<div class="user__ava ava"> <div class="user__ava ava">
<img class="ava__pic" src="{{ course.author.photo.url }}"/> <img class="ava__pic" src="{{ course.author.photo.url }}"/>
@ -69,7 +69,7 @@
{% endif %} {% endif %}
</a> </a>
<div class="user__info"> <div class="user__info">
<a href="{% if course.author %}{% url 'user' course.author.id %}{% endif %}" class="link--black"> <a href="{% if course.author %}{{ course.author.url }}{% endif %}" class="link--black">
<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">

@ -74,7 +74,7 @@
</div> </div>
<div class="course__title title">{{ course.title }}</div> <div class="course__title title">{{ course.title }}</div>
<div class="course__content">{{ course.short_description | safe | linebreaks }}</div> <div class="course__content">{{ course.short_description | safe | linebreaks }}</div>
<a href="{% url 'user' course.author.id %}"> <a href="{{ course.author.url }}">
<div class="course__user user"> <div class="course__user user">
{% if course.author.photo %} {% if course.author.photo %}
<div class="user__ava ava"> <div class="user__ava ava">
@ -274,7 +274,7 @@
</div> </div>
<div class="course__title title">{{ course.title }}</div> <div class="course__title title">{{ course.title }}</div>
<div class="course__content">{{ course.short_description | safe | linebreaks }}</div> <div class="course__content">{{ course.short_description | safe | linebreaks }}</div>
<a href="{% url 'user' course.author.id %}"> <a href="{{ course.author.url }}">
<div class="course__user user user_white"> <div class="course__user user user_white">
{% if course.author.photo %} {% if course.author.photo %}
<div class="user__ava ava"> <div class="user__ava ava">

@ -72,7 +72,7 @@
</div> </div>
<div class="course__title title">{{ course.title }}</div> <div class="course__title title">{{ course.title }}</div>
<div class="course__content">{{ course.short_description | safe | linebreaks }}</div> <div class="course__content">{{ course.short_description | safe | linebreaks }}</div>
<a href="{% url 'user' course.author.id %}"> <a href="{{ course.author.url }}">
<div class="course__user user"> <div class="course__user user">
{% if course.author.photo %} {% if course.author.photo %}
<div class="user__ava ava"> <div class="user__ava ava">

@ -34,7 +34,7 @@
<div> <div>
<div class="lesson__subtitle subtitle">{{ lesson.title }}</div> <div class="lesson__subtitle subtitle">{{ lesson.title }}</div>
<div class="lesson__content">{{ lesson.short_description | safe | linebreaks }}</div> <div class="lesson__content">{{ lesson.short_description | safe | linebreaks }}</div>
<a href="{% url 'user' lesson.author.id %}"> <a href="{{ lesson.author.url }}">
<div class="lesson__user user"> <div class="lesson__user user">
{% if lesson.author.photo %} {% if lesson.author.photo %}
<div class="user__ava ava"> <div class="user__ava ava">

@ -0,0 +1,23 @@
# Generated by Django 2.0.6 on 2018-09-03 22:57
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('payment', '0019_payment_roistat_visit'),
]
operations = [
migrations.CreateModel(
name='UserBonus',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('amount', models.DecimalField(decimal_places=2, default=0, editable=False, max_digits=8)),
('created_at', models.DateTimeField(auto_now_add=True)),
('payment', models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to='payment.Payment')),
],
),
]

@ -0,0 +1,27 @@
# Generated by Django 2.0.6 on 2018-09-03 22:57
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('user', '0024_referral'),
('payment', '0020_userbonus'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='userbonus',
name='referral',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='user.Referral'),
),
migrations.AddField(
model_name='userbonus',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bonuses', to=settings.AUTH_USER_MODEL),
),
]

@ -0,0 +1,19 @@
# Generated by Django 2.0.6 on 2018-09-04 01:06
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('payment', '0021_auto_20180903_2257'),
]
operations = [
migrations.AlterField(
model_name='userbonus',
name='payment',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='payment.Payment'),
),
]

@ -0,0 +1,19 @@
# Generated by Django 2.0.6 on 2018-09-05 23:37
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('payment', '0022_auto_20180904_0106'),
]
operations = [
migrations.AddField(
model_name='payment',
name='bonus',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='purchase_payments', to='payment.UserBonus'),
),
]

@ -1,3 +1,4 @@
from decimal import Decimal
import arrow import arrow
from django.db.models import Func, F from django.db.models import Func, F
@ -102,6 +103,7 @@ class Payment(PolymorphicModel):
roistat_visit = models.PositiveIntegerField('Номер визита Roistat', null=True, editable=False) roistat_visit = models.PositiveIntegerField('Номер визита Roistat', null=True, editable=False)
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
update_at = models.DateTimeField(auto_now=True) update_at = models.DateTimeField(auto_now=True)
bonus = models.ForeignKey('payment.UserBonus', null=True, on_delete=models.SET_NULL, related_name='purchase_payments')
objects = PaymentManger() objects = PaymentManger()
@ -120,18 +122,23 @@ class Payment(PolymorphicModel):
return result.datetime return result.datetime
@classmethod @classmethod
def calc_amount(cls, course_payment=None, school_payment=None, user=None, course=None, date_start=None, weekdays=None): def calc_amount(cls, payment=None, user=None, course=None, date_start=None, weekdays=None):
date_start = date_start or now().date() date_start = date_start or now().date()
date_end = Payment.add_months(date_start, 1) date_end = Payment.add_months(date_start, 1)
if course_payment: if isinstance(payment, CoursePayment):
course = course_payment.course course = payment.course
user = course_payment.user user = payment.user
if school_payment: if isinstance(payment, SchoolPayment):
user = school_payment.user user = payment.user
weekdays = school_payment.weekdays weekdays = payment.weekdays
date_start = school_payment.date_start date_start = payment.date_start
discount = 0
price = 0 price = 0
referral_bonus = 0
referrer_bonus = 0
if hasattr(user, 'referral') and not user.referral.payment:
referral_bonus = user.referral.bonus
referrer_bonus = user.referral.referrer_bonus
discount = 0
if course: if course:
price = course.price price = course.price
else: else:
@ -140,7 +147,6 @@ class Payment(PolymorphicModel):
user=user, user=user,
date_start__lte=date_start, date_start__lte=date_start,
date_end__gte=date_start, date_end__gte=date_start,
add_days=False,
status__in=[ status__in=[
Pingback.PINGBACK_TYPE_REGULAR, Pingback.PINGBACK_TYPE_REGULAR,
Pingback.PINGBACK_TYPE_GOODWILL, Pingback.PINGBACK_TYPE_GOODWILL,
@ -150,8 +156,8 @@ class Payment(PolymorphicModel):
school_schedules_purchased = school_payments.annotate( school_schedules_purchased = school_payments.annotate(
joined_weekdays=Func(F('weekdays'), function='unnest', ) joined_weekdays=Func(F('weekdays'), function='unnest', )
).values_list('joined_weekdays', flat=True).distinct() ).values_list('joined_weekdays', flat=True).distinct()
weekdays = set(map(int, weekdays)) - set(school_schedules_purchased) weekdays = list(set(map(int, weekdays)) - set(school_schedules_purchased))
prev_school_payment = school_payments.last() prev_school_payment = school_payments.filter(add_days=False).last()
add_days = bool(prev_school_payment) add_days = bool(prev_school_payment)
else: else:
add_days = False add_days = False
@ -169,15 +175,20 @@ class Payment(PolymorphicModel):
price = school_schedules.aggregate( price = school_schedules.aggregate(
models.Sum('month_price'), models.Sum('month_price'),
).get('month_price__sum', 0) ).get('month_price__sum', 0)
if not (school_payment and school_payment.id) and price >= config.SERVICE_DISCOUNT_MIN_AMOUNT: if not (payment and payment.id) and price >= config.SERVICE_DISCOUNT_MIN_AMOUNT:
discount = config.SERVICE_DISCOUNT discount = config.SERVICE_DISCOUNT
amount = price - discount amount = price - discount
referral_bonus = round(amount * referral_bonus / 100)
referrer_bonus = round(amount * referrer_bonus / 100)
return { return {
'price': price, 'price': price,
'amount': amount, 'amount': amount,
'referral_bonus': referral_bonus,
'referrer_bonus': referrer_bonus,
'discount': discount, 'discount': discount,
'date_start': date_start, 'date_start': date_start,
'date_end': date_end, 'date_end': date_end,
'weekdays': weekdays,
} }
def calc_commission(self): def calc_commission(self):
@ -199,6 +210,36 @@ class Payment(PolymorphicModel):
Pingback.PINGBACK_TYPE_RISK_REVIEWED_DECLINED, Pingback.PINGBACK_TYPE_RISK_REVIEWED_DECLINED,
] ]
def save(self, *args, **kwargs):
paid = self.status in [Pingback.PINGBACK_TYPE_REGULAR, Pingback.PINGBACK_TYPE_GOODWILL,
Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED,]
amount_data = Payment.calc_amount(payment=self)
print('amount_data', amount_data)
if self.status is None and not self.bonus:
print(123)
self.amount = amount_data.get('amount')
if isinstance(self, SchoolPayment):
self.weekdays = amount_data.get('weekdays')
super().save(*args, **kwargs)
if isinstance(self, CoursePayment) and paid:
author_balance = getattr(self, 'authorbalance', None)
if not author_balance :
AuthorBalance.objects.create(
author=self.course.author,
amount=self.amount,
payment=self,
)
else:
author_balance.amount = self.amount
author_balance.save()
# Если юзер реферал и нет платежа, где применялась скидка
if hasattr(self.user, 'referral') and not self.user.referral.payment and paid:
# Платеж - как сигнал, что скидка применилась
self.user.referral.payment = self
self.user.referral.save()
# Отправляем кэшбэк
self.user.referral.send_bonuses(amount_data.get('referral_bonus'), amount_data.get('referrer_bonus'))
class CoursePayment(Payment): class CoursePayment(Payment):
course = models.ForeignKey(Course, on_delete=models.CASCADE, verbose_name='Курс', related_name='payments') course = models.ForeignKey(Course, on_delete=models.CASCADE, verbose_name='Курс', related_name='payments')
@ -207,22 +248,6 @@ class CoursePayment(Payment):
verbose_name = 'Платеж за курс' verbose_name = 'Платеж за курс'
verbose_name_plural = 'Платежи за курсы' verbose_name_plural = 'Платежи за курсы'
def save(self, *args, **kwargs):
if self.status is None:
amount_data = Payment.calc_amount(course_payment=self)
self.amount = amount_data.get('amount')
super().save(*args, **kwargs)
author_balance = getattr(self, 'authorbalance', None)
if not author_balance:
AuthorBalance.objects.create(
author=self.course.author,
amount=self.amount,
payment=self,
)
else:
author_balance.amount = self.amount
author_balance.save()
class SchoolPayment(Payment): class SchoolPayment(Payment):
weekdays = ArrayField(models.IntegerField(), size=7, verbose_name='Дни недели') weekdays = ArrayField(models.IntegerField(), size=7, verbose_name='Дни недели')
@ -241,12 +266,17 @@ class SchoolPayment(Payment):
]) ])
return days return days
def save(self, *args, **kwargs):
if self.status is None:
amount_data = Payment.calc_amount(school_payment=self)
self.amount = amount_data.get('amount')
super().save(*args, **kwargs)
@property @property
def date_end_humanize(self): def date_end_humanize(self):
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 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)
payment = models.ForeignKey(Payment, on_delete=models.SET_NULL, null=True)
referral = models.ForeignKey('user.Referral', on_delete=models.SET_NULL, null=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('created_at',)

@ -27,7 +27,7 @@ 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 from .models import AuthorBalance, CoursePayment, SchoolPayment, Payment, UserBonus
logger = logging.getLogger('django') logger = logging.getLogger('django')
@ -100,17 +100,18 @@ class SchoolBuyView(TemplateView):
host = urlsplit(self.request.META.get('HTTP_REFERER')) host = urlsplit(self.request.META.get('HTTP_REFERER'))
host = str(host[0]) + '://' + str(host[1]) host = str(host[0]) + '://' + str(host[1])
weekdays = set(request.GET.getlist('weekdays', [])) weekdays = set(request.GET.getlist('weekdays', []))
use_bonuses = request.GET.get('use_bonuses')
roistat_visit = request.COOKIES.get('roistat_visit', None) roistat_visit = request.COOKIES.get('roistat_visit', None)
date_start = request.GET.get('date_start') date_start = request.GET.get('date_start')
date_start = date_start and datetime.datetime.strptime(date_start, '%Y-%m-%d') or now().date() date_start = date_start and datetime.datetime.strptime(date_start, '%Y-%m-%d') or now().date()
if not weekdays: if not weekdays:
messages.error(request, 'Выберите несколько дней недели.') messages.error(request, 'Выберите несколько дней недели.')
return redirect('school:summer-school') return redirect('school:school')
try: try:
weekdays = [int(weekday) for weekday in weekdays] weekdays = [int(weekday) for weekday in weekdays]
except ValueError: except ValueError:
messages.error(request, 'Ошибка выбора дней недели.') messages.error(request, 'Ошибка выбора дней недели.')
return redirect('school:summer-school') return redirect('school:school')
prev_school_payment = SchoolPayment.objects.filter( prev_school_payment = SchoolPayment.objects.filter(
user=request.user, user=request.user,
date_start__lte=date_start, date_start__lte=date_start,
@ -132,6 +133,7 @@ class SchoolBuyView(TemplateView):
add_days=True, add_days=True,
roistat_visit=roistat_visit, roistat_visit=roistat_visit,
) )
# Если произойдет ошибка и оплату бонусами повторят еще раз на те же дни, то вернет ошибку
if school_payment.amount <= 0: if school_payment.amount <= 0:
messages.error(request, 'Выбранные дни отсутствуют в оставшемся периоде подписки') messages.error(request, 'Выбранные дни отсутствуют в оставшемся периоде подписки')
return redirect(reverse_lazy('school:school')) return redirect(reverse_lazy('school:school'))
@ -143,6 +145,19 @@ class SchoolBuyView(TemplateView):
date_start=date_start, date_start=date_start,
date_end=Payment.add_months(date_start), date_end=Payment.add_months(date_start),
) )
if use_bonuses:
if request.user.bonus >= school_payment.amount:
bonus = UserBonus.objects.create(amount= -school_payment.amount, user=request.user, payment=school_payment)
school_payment.amount = 0
school_payment.status = Pingback.PINGBACK_TYPE_REGULAR
else:
bonus = UserBonus.objects.create(amount= -request.user.bonus, user=request.user,
payment=school_payment)
school_payment.amount -= request.user.bonus
school_payment.bonus = bonus
school_payment.save()
if school_payment.status == Pingback.PINGBACK_TYPE_REGULAR:
return redirect(reverse_lazy('payment-success'))
product = Product( product = Product(
f'school_{school_payment.id}', f'school_{school_payment.id}',
school_payment.amount, school_payment.amount,
@ -213,6 +228,7 @@ class PaymentwallCallbackView(View):
product_type_name, product_type_name,
) )
if product_type_name == 'course': if product_type_name == 'course':
properties = { properties = {
'payment_id': payment.id, 'payment_id': payment.id,

@ -63,30 +63,23 @@ class LiveLessonsDetailView(DetailView):
template_name = 'school/livelesson_detail.html' template_name = 'school/livelesson_detail.html'
def get(self, request, pk=None): def get(self, request, pk=None):
response = super().get(request, pk=pk) self.object = self.get_object()
# ??? где проверка? is_purchased = SchoolPayment.objects.filter(
#try: user=request.user,
# school_payment = SchoolPayment.objects.get( date_start__lte=now(),
# user=request.user, date_end__gte=now(),
# add_days=False, status__in=[
# date_start__lte=now(), Pingback.PINGBACK_TYPE_REGULAR,
# date_end__gte=now(), Pingback.PINGBACK_TYPE_GOODWILL,
# status__in=[ Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED,
# Pingback.PINGBACK_TYPE_REGULAR, ],
# Pingback.PINGBACK_TYPE_GOODWILL, weekdays__contains=[self.object.date.weekday() + 1],
# Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED, ).exists()
# ], if not is_purchased and request.user.role not in [User.ADMIN_ROLE, User.TEACHER_ROLE]:
# ) raise Http404
#except SchoolPayment.DoesNotExist:
# school_payment = None context = self.get_context_data(object=self.object)
#if request.user.role not in [User.ADMIN_ROLE, User.TEACHER_ROLE] and not ( return self.render_to_response(context)
# request.user.role == User.USER_ROLE and
# school_payment and
# school_payment.is_deliverable()
#):
# raise Http404
return response
@method_decorator([login_required, is_admin_or_teacher], name='dispatch') @method_decorator([login_required, is_admin_or_teacher], name='dispatch')

@ -36,6 +36,7 @@ class UserEditForm(forms.ModelForm):
'first_name', 'first_name',
'last_name', 'last_name',
'email', 'email',
'slug',
'phone', 'phone',
'city', 'city',
'country', 'country',

@ -0,0 +1,31 @@
# Generated by Django 2.0.6 on 2018-09-03 22:57
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('payment', '0020_userbonus'),
('user', '0023_user_trial_lesson'),
]
operations = [
migrations.CreateModel(
name='Referral',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('bonus', models.IntegerField()),
('referrer_bonus', models.IntegerField()),
('payment', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='payment.Payment')),
('referral', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='referral', to=settings.AUTH_USER_MODEL)),
('referrer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='referrals', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Реферал',
'verbose_name_plural': 'Рефералы',
},
),
]

@ -0,0 +1,18 @@
# Generated by Django 2.0.6 on 2018-09-26 13:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('user', '0023_user_trial_lesson'),
]
operations = [
migrations.AddField(
model_name='user',
name='slug',
field=models.SlugField(allow_unicode=True, blank=True, max_length=100, null=True, unique=True),
),
]

@ -0,0 +1,14 @@
# Generated by Django 2.0.6 on 2018-09-27 23:53
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('user', '0024_referral'),
('user', '0024_user_slug'),
]
operations = [
]

@ -1,4 +1,6 @@
from json import dumps from json import dumps
from django.utils.functional import cached_property
from rest_framework.authtoken.models import Token from rest_framework.authtoken.models import Token
from phonenumber_field.modelfields import PhoneNumberField from phonenumber_field.modelfields import PhoneNumberField
@ -9,10 +11,10 @@ from django.contrib.auth.models import AbstractUser, UserManager as BaseUserMana
from django.contrib.postgres import fields as pgfields from django.contrib.postgres import fields as pgfields
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.urls import reverse
from api.v1 import serializers from api.v1 import serializers
from apps.notification.utils import send_email from apps.notification.utils import send_email
from apps.user.tasks import user_to_mixpanel from apps.user.tasks import user_to_mixpanel
@ -76,6 +78,10 @@ class User(AbstractUser):
photo = models.ImageField('Фото', null=True, blank=True, upload_to='users') photo = models.ImageField('Фото', null=True, blank=True, upload_to='users')
show_in_mainpage = models.BooleanField('Показывать на главной странице', default=False) show_in_mainpage = models.BooleanField('Показывать на главной странице', default=False)
trial_lesson = models.URLField(default='', null=True, blank=True) trial_lesson = models.URLField(default='', null=True, blank=True)
slug = models.SlugField(
allow_unicode=True, null=True, blank=True,
max_length=100, unique=True, db_index=True,
)
objects = UserManager() objects = UserManager()
@ -85,12 +91,16 @@ class User(AbstractUser):
class Meta(AbstractUser.Meta): class Meta(AbstractUser.Meta):
ordering = ('-date_joined',) ordering = ('-date_joined',)
@property
def url(self):
return reverse('user', args=[self.slug or self.id])
def serialized(self): def serialized(self):
user_data = serializers.user.UserSerializer(instance=self).data user_data = serializers.user.UserSerializer(instance=self).data
user_data = dumps(user_data, ensure_ascii=False) user_data = dumps(user_data, ensure_ascii=False)
return user_data return user_data
@property @cached_property
def balance(self): def balance(self):
income = self.balances.filter( income = self.balances.filter(
type=0, type=0,
@ -108,6 +118,10 @@ class User(AbstractUser):
return income_amount - income_commission - payout_amount return income_amount - income_commission - payout_amount
@cached_property
def bonus(self):
return int(self.bonuses.aggregate(models.Sum('amount')).get('amount__sum') or 0)
@receiver(post_save, sender=User) @receiver(post_save, sender=User)
def create_auth_token(sender, instance=None, created=False, **kwargs): def create_auth_token(sender, instance=None, created=False, **kwargs):
@ -261,3 +275,20 @@ class EmailSubscription(models.Model):
email = models.EmailField(_('email address'), unique=True) email = models.EmailField(_('email address'), unique=True)
categories = models.ManyToManyField(SubscriptionCategory) categories = models.ManyToManyField(SubscriptionCategory)
mailchimp_status = models.PositiveSmallIntegerField(choices=MAILCHIMP_STATUS_CHOICES, default=ERROR) mailchimp_status = models.PositiveSmallIntegerField(choices=MAILCHIMP_STATUS_CHOICES, default=ERROR)
class Referral(models.Model):
referral = models.OneToOneField(User, on_delete=models.CASCADE, related_name='referral')
referrer = models.ForeignKey(User, on_delete=models.CASCADE, related_name='referrals')
bonus = models.IntegerField()
referrer_bonus = models.IntegerField()
payment = models.OneToOneField('payment.Payment', null=True, blank=True, on_delete=models.CASCADE)
class Meta:
verbose_name = 'Реферал'
verbose_name_plural = 'Рефералы'
def send_bonuses(self, referral_bonus, referrer_bonus):
from apps.payment.models import UserBonus
UserBonus.objects.create(user=self.referral, amount=referral_bonus, payment=self.payment, referral=self)
UserBonus.objects.create(user=self.referrer, amount=referrer_bonus, payment=self.payment, referral=self)

@ -0,0 +1,14 @@
<div class="section section_gray section_menu">
<div class="section__center center center_xs">
<div class="menu">
<a class="menu__link {% if active == 'profile' %}active{% endif %}"
href="{% url 'user-edit-profile' %}">Профиль</a>
<a class="menu__link {% if active == 'notifications' %}active{% endif %}"
href="{% url 'user-edit-notifications' %}">Уведомления</a>
<a class="menu__link {% if active == 'payments' %}active{% endif %}"
href="{% url 'user-edit-payments' %}">Платежи</a>
<!--<a class="menu__link {% if active == 'bonuses' %}active{% endif %}"-->
<!--href="{% url 'user-bonuses' %}">Бонусы</a>-->
</div>
</div>
</div>

@ -0,0 +1,71 @@
{% extends "templates/lilcity/index.html" %}
{% load static %}
{% load rupluralize from plural %}
{% block content %}
{% include "../blocks/profile-menu.html" with active="bonuses" %}
<div class="section section_gray" style="padding-bottom: 0;">
<div class="section__center center center_xs">
<div class="title title_sm">Бонусы</div>
<div class="text">У вас {{ request.user.bonus|rupluralize:"бонус,бонуса,бонусов" }}</div>
<div>
Вы можете пригласить своих друзей в наш сервис, за это
{% if config.REFERRER_BONUS == config.REFERRAL_BONUS %}
вы и они получите по {{ config.REFERRER_BONUS }}%
{% else %}
вы получите {{ config.REFERRER_BONUS }}%, а они {{ config.REFERRAL_BONUS }}%
{% endif %}
от суммы их первой покупки на бонусный счет. Приглашайте друзей, накапливайте бонусные баллы, тратьте бонусы
на приобретения доступа к школе и курсам. Оплата услуг возможна только, если вы накопили баллов на стоимость услуги.
<br>
Просто отправьте им эту ссылку, по которой они смогут зарегистрироваться:
</div>
<div class="form__field field" style="margin-bottom: 0;">
<div class="field__wrap">
<input id="referrer-url" class="field__input" type="text" value="{{ referrer_url }}" readonly="readonly">
</div>
</div>
</div>
</div>
<div class="section section_gray">
<div class="section__center center center_sm">
<div class="title title_sm">История бонусов</div>
<div class="transactions transactions_bonuses">
<div class="transactions__wrap">
<div class="transactions__row">
<div class="transactions__cell transactions__product">Продукт</div>
<div class="transactions__cell transactions__amount">Сумма покупки</div>
<div class="transactions__cell transactions__user">Реферал</div>
<div class="transactions__cell transactions__amount">Сумма бонусов</div>
</div>
{% for bonus in bonuses %}
<div class="transactions__row">
{% with payment=bonus.payment %}
{% if payment.course %}
<div class="transactions__cell transactions__product">Курс. {{ payment.course.title }}</div>
{% else %}
<div class="transactions__cell transactions__product">
{% if request.user_agent.is_mobile %}
Школа. {% if payment.date_start and payment.date_end %}{{ payment.date_start|date:"j b" }} - {{ payment.date_end|date:"j b" }}{% endif %}
{% else %}
Школа. {% if payment.date_start and payment.date_end %}{{ payment.date_start }} - {{ payment.date_end }}{% endif %}
{% endif %}
</div>
{% endif %}
<div class="transactions__cell transactions__amount">{{payment.amount }}</div>
<div class="transactions__cell transactions__user">
{% if bonus.referral %}{{ bonus.referral.referral.get_full_name }}{% endif %}
</div>
<div class="transactions__cell transactions__amount">{{ bonus.amount|floatformat }}</div>
{% endwith %}
</div>
{% empty %}
Нет истории бонусов
{% endfor %}
</div>
</div>
</div>
</div>
{% endblock content %}

@ -1,13 +1,5 @@
{% extends "templates/lilcity/index.html" %} {% load static %} {% block content %} {% extends "templates/lilcity/index.html" %} {% load static %} {% block content %}
<div class="section section_gray section_menu"> {% include "../blocks/profile-menu.html" with active="notifications" %}
<div class="section__center center center_xs">
<div class="menu">
<a class="menu__link" href="{% url 'user-edit-profile' %}">Профиль</a>
<a class="menu__link active" href="{% url 'user-edit-notifications' user.id %}">Уведомления</a>
<a class="menu__link" href="{% url 'user-edit-payments' user.id %}">Платежи</a>
</div>
</div>
</div>
{% if not user.is_email_proved %} {% if not user.is_email_proved %}
<div class="section section_confirm"> <div class="section section_confirm">
<div class="section__center center center_xs"> <div class="section__center center center_xs">

@ -1,13 +1,5 @@
{% extends "templates/lilcity/index.html" %} {% load static %} {% block content %} {% extends "templates/lilcity/index.html" %} {% load static %} {% block content %}
<div class="section section_gray section_menu"> {% include "../blocks/profile-menu.html" with active="payments" %}
<div class="section__center center center_xs">
<div class="menu">
<a class="menu__link" href="{% url 'user-edit-profile' %}">Профиль</a>
<a class="menu__link" href="{% url 'user-edit-notifications' user.id %}">Уведомления</a>
<a class="menu__link active" href="{% url 'user-edit-payments' user.id %}">Платежи</a>
</div>
</div>
</div>
{% if not user.is_email_proved %} {% if not user.is_email_proved %}
<div class="section section_confirm"> <div class="section section_confirm">
<div class="section__center center center_xs"> <div class="section__center center center_xs">
@ -73,42 +65,42 @@
<div class="section section_gray"> <div class="section section_gray">
<div class="section__center center center_xs"> <div class="section__center center center_xs">
<div class="title title_sm">История платежей</div> <div class="title title_sm">История платежей</div>
<div class="transactions"> <div class="transactions transactions_payments">
<div class="transactions__wrap"> <div class="transactions__wrap">
{% if request.user.payments.all.exists %} {% if request.user.payments.all.exists %}
{% for payment in request.user.payments.all %} {% for payment in request.user.payments.all %}
<div class="transactions__row"> <div class="transactions__row">
{% if payment.course %} {% if payment.course %}
<div class="transactions__cell">Курс. {{payment.course.title}}</div> <div class="transactions__cell transactions__product">Курс. {{payment.course.title}}</div>
{% else %} {% else %}
<div class="transactions__cell"> <div class="transactions__cell transactions__product">
Школа. {% if payment.date_start and payment.date_end %}{{ payment.date_start }} - {{ payment.date_end }}{% endif %} Школа. {% if payment.date_start and payment.date_end %}{{ payment.date_start }} - {{ payment.date_end }}{% endif %}
</div> </div>
{% endif %} {% endif %}
{% if payment.balance %} {% if payment.balance %}
<div class="transactions__cell">{{payment.balance.amount}}</div> <div class="transactions__cell transactions__amount">{{payment.balance.amount}}</div>
{% else %} {% else %}
<div class="transactions__cell">{{payment.amount}}</div> <div class="transactions__cell transactions__amount">{{payment.amount}}</div>
{% endif %} {% endif %}
{% if payment.balance.type == 1 %} {% if payment.balance.type == 1 %}
<div class="transactions__cell"> <div class="transactions__cell transactions__status">
{% if payment.balance.status == 0 %} {% if payment.balance.status == 0 %}
<span class="transactions__cell__pending">Ожидается подтверждение выплаты</span> <span class="transactions__status_pending">Ожидается подтверждение выплаты</span>
{% elif payment.balance.status == 1 %} {% elif payment.balance.status == 1 %}
<span class="transactions__cell__success">Выплачено</span> <span class="transactions__status_success">Выплачено</span>
{% else %} {% else %}
<span class="transactions__cell__error">Выплата отменена</span> <span class="transactions__status_error">Выплата отменена</span>
Причина: "{{ payment.balance.cause }} Причина: "{{ payment.balance.cause }}
{% endif %} {% endif %}
</div> </div>
{% else %} {% else %}
<div class="transactions__cell"> <div class="transactions__cell transactions__status">
{% if payment.is_deliverable %} {% if payment.is_deliverable %}
<span class="transactions__cell__success">Оплачено</span> <span class="transactions__status_success">Оплачено</span>
{% elif payment.is_under_review %} {% elif payment.is_under_review %}
<span class="transactions__cell__pending">Ожидается подтверждение оплаты</span> <span class="transactions__status_pending">Ожидается подтверждение оплаты</span>
{% else %} {% else %}
<span class="transactions__cell__error">Ошибка оплаты</span> <span class="transactions__status_error">Ошибка оплаты</span>
{% endif %} {% endif %}
</div> </div>
{% endif %} {% endif %}

@ -1,13 +1,9 @@
{% extends "templates/lilcity/index.html" %} {% load static %} {% load thumbnail %} {% block content %} {% extends "templates/lilcity/index.html" %}
<div class="section section_gray section_menu"> {% load static %}
<div class="section__center center center_xs"> {% load settings %}
<div class="menu"> {% load thumbnail %}
<a class="menu__link active" href="{% url 'user-edit-profile' %}">Профиль</a> {% block content %}
<a class="menu__link" href="{% url 'user-edit-notifications' user.id %}">Уведомления</a> {% include "../blocks/profile-menu.html" with active="profile" %}
<a class="menu__link" href="{% url 'user-edit-payments' user.id %}">Платежи</a>
</div>
</div>
</div>
{% if not user.is_email_proved %} {% if not user.is_email_proved %}
<div class="section section_confirm"> <div class="section section_confirm">
<div class="section__center center center_xs"> <div class="section__center center center_xs">
@ -44,7 +40,7 @@
{% empty %} {% empty %}
<img id="photo" class="ava__pic" src="{% static 'img/user_default.jpg' %}" width="120px" height="120px" /> <img id="photo" class="ava__pic" src="{% static 'img/user_default.jpg' %}" width="120px" height="120px" />
{% endthumbnail %} {% endthumbnail %}
<input name="photo" class="ava__input" type="file" accept='image/*' onchange='openFile(event)'> <input id="user-photo-upload" name="photo" class="ava__input" type="file" accept='image/*'>
<div class="ava__icon"> <div class="ava__icon">
<svg class="icon icon-photo"> <svg class="icon icon-photo">
<use xlink:href="{% static 'img/sprite.svg' %}#icon-photo"></use> <use xlink:href="{% static 'img/sprite.svg' %}#icon-photo"></use>
@ -55,7 +51,7 @@
<div class="form__field field{% if form.first_name.errors %} error{% endif %}"> <div class="form__field field{% if form.first_name.errors %} error{% endif %}">
<div class="field__label">ИМЯ</div> <div class="field__label">ИМЯ</div>
<div class="field__wrap"> <div class="field__wrap">
<input name='first_name' class="field__input" type="text" placeholder="Имя" value="{{ user.first_name }}"> <input name='first_name' id="user-first-name" class="field__input" type="text" placeholder="Имя" value="{{ user.first_name }}">
</div> </div>
{% for error in form.first_name.errors %} {% for error in form.first_name.errors %}
<div class="field__error">{{ error }}</div> <div class="field__error">{{ error }}</div>
@ -64,7 +60,7 @@
<div class="form__field field{% if form.last_name.errors %} error{% endif %}"> <div class="form__field field{% if form.last_name.errors %} error{% endif %}">
<div class="field__label">ФАМИЛИЯ</div> <div class="field__label">ФАМИЛИЯ</div>
<div class="field__wrap"> <div class="field__wrap">
<input name='last_name' class="field__input" type="text" placeholder="Фамилия" value="{{ user.last_name }}"> <input name='last_name' id="user-last-name" class="field__input" type="text" placeholder="Фамилия" value="{{ user.last_name }}">
</div> </div>
{% for error in form.last_name.errors %} {% for error in form.last_name.errors %}
<div class="field__error">{{ error }}</div> <div class="field__error">{{ error }}</div>
@ -89,6 +85,17 @@
<div class="field__error">{{ error }}</div> <div class="field__error">{{ error }}</div>
{% endfor %} {% endfor %}
</div> </div>
<div class="form__field field{% if form.slug.errors %} error{% endif %}">
<div class="field__label">Ссылка</div>
<div class="field__wrap field__wrap__appended">
<div class="field__text">{% setting 'MAIN_HOST' %}/user/</div>
<input name='slug' id="user-slug" class="field__input field__input__appended" type="text"
value="{{ user.slug|default:'' }}" data-current-slug="{{ user.slug|default:'' }}">
</div>
{% for error in form.slug.errors %}
<div class="field__error">{{ error }}</div>
{% endfor %}
</div>
<div class="form__fieldset"> <div class="form__fieldset">
<div class="form__field field{% if form.city.errors %} error{% endif %}"> <div class="form__field field{% if form.city.errors %} error{% endif %}">
<div class="field__label">ГОРОД</div> <div class="field__label">ГОРОД</div>
@ -257,19 +264,6 @@
</div> </div>
</div> </div>
</div> </div>
<script>
var openFile = function(file) {
var input = file.target;
var reader = new FileReader();
reader.onload = function(){
var dataURL = reader.result;
var output = document.getElementById('photo');
output.src = dataURL;
};
reader.readAsDataURL(input.files[0]);
};
</script>
{% endblock content %} {% endblock content %}

@ -4,18 +4,16 @@ from uuid import uuid4
from os.path import splitext from os.path import splitext
from datetime import timedelta from datetime import timedelta
from paymentwall import Pingback from paymentwall import Pingback
import short_url
from django.conf import settings from django.conf import settings
from django.contrib.auth import login from django.contrib.auth import login
from django.core.exceptions import ValidationError from django.shortcuts import reverse, redirect
from django.shortcuts import render, reverse, redirect
from django.views import View from django.views import View
from django.views.generic import DetailView, UpdateView, TemplateView, FormView from django.views.generic import DetailView, UpdateView, TemplateView, FormView
from django.contrib import messages from django.contrib import messages
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.hashers import check_password, make_password
from django.http import Http404
from django.db.models import F, Func, Sum, Min, Max from django.db.models import F, Func, Sum, Min, Max
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
@ -99,6 +97,7 @@ class ProfileView(TemplateView):
class UserView(DetailView): class UserView(DetailView):
model = User model = User
template_name = 'user/author_profile.html' template_name = 'user/author_profile.html'
query_pk_and_slug = True
def get_context_data(self, object): def get_context_data(self, object):
context = super().get_context_data() context = super().get_context_data()
@ -146,7 +145,7 @@ class NotificationEditView(TemplateView):
request.user.email_subscription.categories.set( request.user.email_subscription.categories.set(
SubscriptionCategory.objects.filter(id__in=categories) SubscriptionCategory.objects.filter(id__in=categories)
) )
return redirect('user-edit-notifications', request.user.id) return redirect('user-edit-notifications')
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
@ -183,7 +182,7 @@ class PaymentHistoryView(FormView):
return self.form_invalid(form) return self.form_invalid(form)
def get_success_url(self): def get_success_url(self):
success_url = reverse_lazy('user-edit-payments', args=[self.request.user.id]) success_url = reverse_lazy('user-edit-payments')
return success_url return success_url
@ -292,3 +291,17 @@ class AuthorRequestView(FormView):
'facebook': self.request.user.facebook, 'facebook': self.request.user.facebook,
} }
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
@method_decorator(login_required, name='dispatch')
class BonusHistoryView(TemplateView):
model = User
template_name = 'user/bonus-history.html'
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
context['bonuses'] = request.user.bonuses.all()
context['referrer_url'] = '%s%s?referrer=%s' % (
settings.MAIN_HOST, reverse('index'), short_url.encode_url(request.user.id)
)
return self.render_to_response(context)

@ -1,11 +1,14 @@
from django.db.models import Func, F from django.db.models import Func, F
from django.utils.timezone import now from django.utils.timezone import now
from paymentwall.pingback import Pingback from paymentwall.pingback import Pingback
from django.contrib.auth import get_user_model
from django.conf import settings
from apps.config.models import Config from apps.config.models import Config
from apps.content.models import Baner from apps.content.models import Baner
from apps.payment.models import SchoolPayment from apps.payment.models import SchoolPayment
User = get_user_model()
def config(request): def config(request):
return {"config": Config.load()} return {"config": Config.load()}
@ -39,3 +42,17 @@ def school_purchased(request):
'is_school_purchased': False, 'is_school_purchased': False,
'school_schedules_purchased': [], 'school_schedules_purchased': [],
} }
def referrer(request):
referrer = request.session.get('referrer')
if referrer:
try:
referrer = User.objects.get(pk=referrer)
except:
referrer = None
return {'referrer': referrer}
def settings(request):
return {'settings': settings}

@ -31,6 +31,7 @@ SECRET_KEY = os.getenv('SECRET_KEY', 'jelm*91lj(_-o20+6^a+bgv!4s6e_efry^#+f#=1ak
DEBUG = os.getenv('DEBUG', False) DEBUG = os.getenv('DEBUG', False)
ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS', '*').split(',') ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS', '*').split(',')
MAIN_HOST = os.getenv('MAIN_HOST', 'lil.school')
# Application definition # Application definition
@ -95,6 +96,8 @@ TEMPLATES = [
'project.context_processors.config', 'project.context_processors.config',
'project.context_processors.baner', 'project.context_processors.baner',
'project.context_processors.school_purchased', 'project.context_processors.school_purchased',
'project.context_processors.referrer',
'project.context_processors.settings',
'django.template.context_processors.debug', 'django.template.context_processors.debug',
'django.template.context_processors.request', 'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth', 'django.contrib.auth.context_processors.auth',

@ -20,7 +20,13 @@
}, },
components: {}, components: {},
urls: { urls: {
courses: "{% url 'courses' %}" courses: "{% url 'courses' %}",
userProfileEdit: "{% url 'user-edit-profile' %}",
},
flags: {
referrer: '{{ referrer.id|default:'' }}',
referrerName: '{% if referrer %}{{ referrer.get_full_name }}{% endif %}',
isReferralUrl: {{ is_referral_url|yesno:"true,false" }},
}, },
}; };
</script> </script>

@ -10,13 +10,13 @@
<div class="auth js-auth"> <div class="auth js-auth">
<div class="auth__login js-auth-login"> <div class="auth__login js-auth-login">
<div class="auth__nav"> <div class="auth__nav">
<a class="auth__type js-auth-type active" href="#">Войти</a> <a class="auth__type js-auth-type_login js-auth-type" href="#">Войти</a>
<a class="auth__type js-auth-type" href="#">РЕГИСТРАЦИЯ</a> <a class="auth__type js-auth-type_registration js-auth-type" href="#">РЕГИСТРАЦИЯ</a>
</div> </div>
<div class="auth__body"> <div class="auth__body">
<form id="learner-auth-form" method="post" action="{% url 'lilcity:login' %}"> <form id="learner-auth-form" method="post" action="{% url 'lilcity:login' %}">
{% csrf_token %} {% csrf_token %}
<div class="auth__tab js-auth-tab" style="display: block;"> <div class="auth__tab js-auth-tab js-auth-tab_login">
<div class="auth__enter js-auth-enter"> <div class="auth__enter js-auth-enter">
<div id="learner-auth-field__username" class="auth__field field learner-auth-form__field"> <div id="learner-auth-field__username" class="auth__field field learner-auth-form__field">
<div class="field__label">ПОЧТА</div> <div class="field__label">ПОЧТА</div>
@ -63,7 +63,15 @@
</form> </form>
<form id="learner-registration-form" method="post" action="{% url 'lilcity:registration-learner' %}"> <form id="learner-registration-form" method="post" action="{% url 'lilcity:registration-learner' %}">
{% csrf_token %} {% csrf_token %}
<div class="auth__tab js-auth-tab"> <div class="auth__tab js-auth-tab js-auth-tab_registration">
{% if referrer %}
<input type="hidden" name="referrer" value="{{ referrer.id }}">
<div class="auth__text">
Спасибо за&nbsp;то, что&nbsp;вы&nbsp;с&nbsp;нами! Вас&nbsp;пригласил ваш&nbsp;друг {{ referrer.get_full_name }}.
При&nbsp;первой покупке {{ config.REFERRAL_BONUS }}% от&nbsp;суммы услуги будут зачислены на&nbsp;ваш бонусный счет.
Приятного обучения!
</div>
{% endif %}
<div class="auth__fieldset"> <div class="auth__fieldset">
<div id="learner-registration-field__first-name" class="auth__field field learner-registration-form__field"> <div id="learner-registration-field__first-name" class="auth__field field learner-registration-form__field">
<div class="field__label">ИМЯ</div> <div class="field__label">ИМЯ</div>

@ -70,10 +70,21 @@
<div class="order__label">ШКОЛА</div> <div class="order__label">ШКОЛА</div>
<div class="order__dates"></div> <div class="order__dates"></div>
<div class="order__days"></div> <div class="order__days"></div>
{% if request.user.bonuses %}
<label class="buy__bonuses switch" style="display: none;">
<input class="switch__input" type="checkbox" data-bonuses="{{ request.user.bonus }}">
<span class="switch__content">
<span class="switch__cell">Использовать <span class="buy__bonuses-count"></span></span>
</span>
</label>
{% endif %}
</div> </div>
<div class="order__foot"> <div class="order__foot">
<div class="order__subtitle">Итого, за месяц:</div> <div class="order__subtitle">Итого, за месяц:</div>
<div class="order__total order_price_text"></div> <div class="order__total">
<div class="loading-loader"></div>
<span class="order_price_text"></span>
</div>
</div> </div>
</div> </div>
</div> </div>

@ -18,7 +18,7 @@
<div class="teachers__wrap"> <div class="teachers__wrap">
<div class="teachers__title"> <div class="teachers__title">
<div class="teachers__title-name"> <div class="teachers__title-name">
<a href="{% url 'user' teacher.id %}">{{ teacher.get_full_name }}</a>{% if teacher.instagram_hashtag %}, <a href="{{ teacher.url }}">{{ teacher.get_full_name }}</a>{% if teacher.instagram_hashtag %},
<a href='https://www.instagram.com/explore/tags/{{ teacher.instagram_hashtag }}/' target="_blank"> <a href='https://www.instagram.com/explore/tags/{{ teacher.instagram_hashtag }}/' target="_blank">
{{ teacher.instagram_hashtag }} {{ teacher.instagram_hashtag }}
</a> </a>

@ -1,4 +1,5 @@
{% load static %} {% load thumbnail %} {% load static %} {% load thumbnail %}
{% load rupluralize from plural %}
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<div class="header__login"> <div class="header__login">
<a class="header__ava ava" href="{% if request.user_agent.is_touch_capable %}#{% else %}{% url 'user-profile' %}{% endif %}"> <a class="header__ava ava" href="{% if request.user_agent.is_touch_capable %}#{% else %}{% url 'user-profile' %}{% endif %}">
@ -9,22 +10,29 @@
{% endthumbnail %} {% endthumbnail %}
</a> </a>
<div class="header__drop"> <div class="header__drop">
{# FIXME #}
{% if request.user.role >= request.user.AUTHOR_ROLE %} {% if request.user.role >= request.user.AUTHOR_ROLE %}
<a class="header__link header__link_border" href="{% url 'user-edit-payments' request.user.id %}"> <a class="header__link header__link_border" href="{% url 'user-edit-payments' %}">
{{ request.user.balance }} руб. {{ request.user.balance }} руб.
</a> </a>
{% if request.user.auth_token %}
{% if request.user.role >= request.user.AUTHOR_ROLE %}
<a class="header__link header__link_green" href="{% url 'course_create' %}">
<div class="header__title">ДОБАВИТЬ КУРС</div>
</a>
{% endif %}
{% if request.user.role >= request.user.TEACHER_ROLE %}
<a class="header__link" href="{% url 'school:lessons' %}">
<div class="header__title">УРОКИ ШКОЛЫ</div>
</a>
{% endif %}
{% endif %} {% endif %}
{% comment %}
FIXME
<!--<a class="header__link header__link_border" href="{% url 'user-bonuses' %}">-->
<!--{{ request.user.bonus|rupluralize:"бонус,бонуса,бонусов" }}-->
<!--</a>-->
{% endcomment %}
{% if request.user.auth_token %}
{% if request.user.role >= request.user.AUTHOR_ROLE %}
<a class="header__link header__link_green" href="{% url 'course_create' %}">
<div class="header__title">ДОБАВИТЬ КУРС</div>
</a>
{% endif %}
{% if request.user.role >= request.user.TEACHER_ROLE %}
<a class="header__link" href="{% url 'school:lessons' %}">
<div class="header__title">УРОКИ ШКОЛЫ</div>
</a>
{% endif %}
{% endif %} {% endif %}
<a class="header__link" href="{% url 'user-profile' %}"> <a class="header__link" href="{% url 'user-profile' %}">
<div class="header__title">ПРОФИЛЬ</div> <div class="header__title">ПРОФИЛЬ</div>

@ -151,6 +151,7 @@
{% include "templates/blocks/popup_subscribe.html" %} {% include "templates/blocks/popup_subscribe.html" %}
</div> </div>
{% include 'templates/blocks/lil_store_js.html' %} {% include 'templates/blocks/lil_store_js.html' %}
<script type="text/javascript" src={% static "app.js" %}></script> <script type="text/javascript" src={% static "app.js" %}></script>
<script> <script>
var schoolDiscount = parseFloat({{ config.SERVICE_DISCOUNT }}); var schoolDiscount = parseFloat({{ config.SERVICE_DISCOUNT }});
@ -161,5 +162,6 @@
(function(w, d, s, h, id) { w.roistatProjectId = id; w.roistatHost = h; var p = d.location.protocol == "https:" ? "https://" : "http://"; var u = /^.*roistat_visit=[^;]+(.*)?$/.test(d.cookie) ? "/dist/module.js" : "/api/site/1.0/"+id+"/init"; var js = d.createElement(s); js.charset="UTF-8"; js.async = 1; js.src = p+h+u; var js2 = d.getElementsByTagName(s)[0]; js2.parentNode.insertBefore(js, js2);})(window, document, 'script', 'cloud.roistat.com', '{% setting "ROISTAT_COUNTER_ID" %}'); (function(w, d, s, h, id) { w.roistatProjectId = id; w.roistatHost = h; var p = d.location.protocol == "https:" ? "https://" : "http://"; var u = /^.*roistat_visit=[^;]+(.*)?$/.test(d.cookie) ? "/dist/module.js" : "/api/site/1.0/"+id+"/init"; var js = d.createElement(s); js.charset="UTF-8"; js.async = 1; js.src = p+h+u; var js2 = d.getElementsByTagName(s)[0]; js2.parentNode.insertBefore(js, js2);})(window, document, 'script', 'cloud.roistat.com', '{% setting "ROISTAT_COUNTER_ID" %}');
</script> </script>
{% block foot %}{% endblock foot %} {% block foot %}{% endblock foot %}
{% block body_js %}{% endblock body_js %}
</body> </body>
</html> </html>

@ -30,7 +30,7 @@ from apps.user.views import (
ProfileEditView, NotificationEditView, ProfileEditView, NotificationEditView,
PaymentHistoryView, resend_email_verify, PaymentHistoryView, resend_email_verify,
SubscribeView, ProfileView, SubscribeView, ProfileView,
) BonusHistoryView)
from apps.payment.views import ( from apps.payment.views import (
CourseBuySuccessView, CourseBuyView, CourseBuySuccessView, CourseBuyView,
PaymentwallCallbackView, SchoolBuySuccessView, PaymentwallCallbackView, SchoolBuySuccessView,
@ -70,8 +70,10 @@ urlpatterns = [
path('user/profile/', ProfileView.as_view(), name='user-profile'), path('user/profile/', ProfileView.as_view(), name='user-profile'),
path('user/profile/edit', ProfileEditView.as_view(), name='user-edit-profile'), path('user/profile/edit', ProfileEditView.as_view(), name='user-edit-profile'),
path('user/<int:pk>/', UserView.as_view(), name='user'), path('user/<int:pk>/', UserView.as_view(), name='user'),
path('user/<int:pk>/notifications', NotificationEditView.as_view(), name='user-edit-notifications'), path('user/<str:slug>/', UserView.as_view(), name='user'),
path('user/<int:pk>/payments', PaymentHistoryView.as_view(), name='user-edit-payments'), path('user/notifications', NotificationEditView.as_view(), name='user-edit-notifications'),
path('user/payments', PaymentHistoryView.as_view(), name='user-edit-payments'),
path('user/bonuses', BonusHistoryView.as_view(), name='user-bonuses'),
path('user/resend-email-verify', resend_email_verify, name='resend-email-verify'), path('user/resend-email-verify', resend_email_verify, name='resend-email-verify'),
path('subscribe', SubscribeView.as_view(), name='subscribe'), path('subscribe', SubscribeView.as_view(), name='subscribe'),
path('subscribe/success', TemplateView.as_view(template_name='templates/lilcity/subscribe_success.html'), name='subscribe-success'), path('subscribe/success', TemplateView.as_view(template_name='templates/lilcity/subscribe_success.html'), name='subscribe-success'),

@ -1,5 +1,6 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
import short_url
from django.db.models import Min, Func, F from django.db.models import Min, Func, F
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.views.generic import TemplateView from django.views.generic import TemplateView
@ -21,6 +22,8 @@ class IndexView(TemplateView):
template_name = 'templates/lilcity/main.html' template_name = 'templates/lilcity/main.html'
def get_context_data(self): def get_context_data(self):
referrer = self.request.GET.get('referrer')
is_referral_url = bool(referrer)
context = super().get_context_data() context = super().get_context_data()
now_time = now() now_time = now()
@ -64,7 +67,19 @@ class IndexView(TemplateView):
school_payment_exists = False school_payment_exists = False
school_schedules_purchased = [] school_schedules_purchased = []
if referrer and not self.request.user.is_authenticated:
try:
referrer = short_url.decode_url(referrer)
referrer = User.objects.get(pk=referrer)
except:
referrer = None
self.request.session['referrer'] = referrer and referrer.id
else:
referrer = None
context.update({ context.update({
'referrer': referrer,
'is_referral_url': is_referral_url,
'online': online, 'online': online,
'online_coming_soon': online_coming_soon, 'online_coming_soon': online_coming_soon,
'school_schedule': school_schedule, 'school_schedule': school_schedule,

@ -31,3 +31,4 @@ ua-parser==0.8.0
django-ipware django-ipware
django-imagekit django-imagekit
pusher==2.0.1 pusher==2.0.1
short_url

@ -17,7 +17,7 @@ import "./modules/courses";
import "./modules/comments"; import "./modules/comments";
import "./modules/comments"; import "./modules/comments";
import "./modules/password-show"; import "./modules/password-show";
import "./modules/profile"; import {main as profileMain} from "./modules/profile";
import "./modules/notification"; import "./modules/notification";
import "./modules/mixpanel"; import "./modules/mixpanel";
@ -55,5 +55,15 @@ const app = new Vue({
store: window.LIL_STORE, store: window.LIL_STORE,
} }
}, },
mounted(){
if(this.urlIs('userProfileEdit')){
profileMain(this);
}
},
methods: {
urlIs(urlPatternName){
return window.location.pathname.search(this.store.urls[urlPatternName]) > -1;
},
},
components: components components: components
}); });

@ -41,7 +41,7 @@ $(document).ready(function () {
pass.hide(); pass.hide();
// login.fadeIn(); // login.fadeIn();
window.location.reload(); window.location.href = '/';
}); });
$('#password-reset__success-hide').on('click', function (e) { $('#password-reset__success-hide').on('click', function (e) {
@ -246,7 +246,7 @@ $(document).ready(function () {
window.location.href = nextUrl; window.location.href = nextUrl;
} }
else{ else{
window.location.reload(); window.location = '/';
} }
} else { } else {
registrationButton.removeClass('loading'); registrationButton.removeClass('loading');

@ -1,13 +1,17 @@
import $ from 'jquery'; import $ from 'jquery';
import {showNotification} from "./notification"
import Inputmask from "inputmask"; import Inputmask from "inputmask";
import SmoothScroll from 'smooth-scroll/dist/js/smooth-scroll'; import SmoothScroll from 'smooth-scroll/dist/js/smooth-scroll';
import baguetteBox from 'baguettebox.js' import baguetteBox from 'baguettebox.js'
import createHistory from 'history/createBrowserHistory'
window.$ = window.jQuery = jQuery = $; window.$ = window.jQuery = jQuery = $;
window.Inputmask = Inputmask; window.Inputmask = Inputmask;
window.baguetteBox = baguetteBox; window.baguetteBox = baguetteBox;
$(document).ready(function () { $(document).ready(function () {
const $popupAuth = $('.js-popup-auth');
let authPopupActiveTab = 'login';
baguetteBox.run('.gallery'); baguetteBox.run('.gallery');
// Добавляем заголовок X-CSRFToken для всех AJAX запросов JQuery. // Добавляем заголовок X-CSRFToken для всех AJAX запросов JQuery.
$.ajaxSetup({ $.ajaxSetup({
@ -40,6 +44,23 @@ $(document).ready(function () {
$('.banner').slideUp(); $('.banner').slideUp();
}) })
}); });
if(window.LIL_STORE.flags.isReferralUrl){
if(window.LIL_STORE.user.id){
showNotification('error', 'Извините, вы уже зарегестрировались на нашем сервисе, получить бонусные баллы возможно только для новых пользователей');
// Создаем объект history API
const history = createHistory();
history.replace('/');
}
else if(window.LIL_STORE.flags.referrer){
authPopupActiveTab = 'registration';
setTimeout(() => {
$('.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();
}); });
function updateHeader() { function updateHeader() {
@ -67,4 +88,4 @@ function updateHeader() {
headerElement.css('top', ''); headerElement.css('top', '');
} }
} }
} }

@ -1,6 +1,7 @@
import $ from 'jquery'; import $ from 'jquery';
import moment from 'moment'; import moment from 'moment';
import {api} from './api'; import {api} from './api';
import {rupluralize} from '../utils';
moment.locale('ru'); moment.locale('ru');
@ -35,7 +36,7 @@ $(document).ready(function () {
}); });
let body = $('body'), let body = $('body'),
popup; popup = $('.popup.visible.open');
body.on('click', '[data-popup]', function(e){ body.on('click', '[data-popup]', function(e){
const $this = $(this); const $this = $(this);
@ -149,14 +150,24 @@ $(document).ready(function () {
updateCart(); updateCart();
}); });
$(document).on('change', '[data-bonuses]', function(){
updateCart();
});
function updateCart(){ function updateCart(){
var link = $('.but_btn_popup').data('link'); var link = $('.but_btn_popup').data('link');
var $order = $('.order');
var $orderPrice = $('.order_price_text'); var $orderPrice = $('.order_price_text');
var $orderDates = $('.order__dates'); var $orderDates = $('.order__dates');
var dateStart = popup.data('date-start'); var dateStart = popup.data('date-start');
var days = ['', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота', 'Воскресенье']; var days = ['', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота', 'Воскресенье'];
var weekdays = []; var weekdays = [];
var daysText = []; var daysText = [];
var $bonuses = $('.buy__bonuses');
var $bonusesCheckbox = $('[data-bonuses]');
var $bonusesCount = $('.buy__bonuses-count');
var useBonuses = $bonusesCheckbox.prop('checked');
var bonuses = +$bonusesCheckbox.data('bonuses');
$('[data-day]').each(function() { $('[data-day]').each(function() {
var weekday = $(this).data('day'); var weekday = $(this).data('day');
if($(this).is(':checked')) { if($(this).is(':checked')) {
@ -166,25 +177,40 @@ $(document).ready(function () {
}); });
if(weekdays.length){ if(weekdays.length){
$order.addClass('order--loading');
$orderPrice.html('');
api.getPaymentAmount({ user: window.LIL_STORE.user.id, weekdays: weekdays, date_start: dateStart}) api.getPaymentAmount({ user: window.LIL_STORE.user.id, weekdays: weekdays, date_start: dateStart})
.then((response) => { .then(response => {
$order.removeClass('order--loading');
var text = ''; var text = '';
if(response.data.price != response.data.amount) { var amount = +response.data.amount;
text = '<del>'+response.data.price+'</del> '+response.data.amount+'р.'; if(bonuses){
$bonusesCount.text(rupluralize(bonuses > amount ? amount : bonuses, ['бонус', 'бонуса', 'бонусов']));
$bonuses.show();
if(useBonuses) {
amount = bonuses > amount ? 0 : amount - bonuses;
}
}
if(response.data.price != amount) {
text = '<del>' + response.data.price+'</del> ' + amount + 'р.';
} else { } else {
text = response.data.amount+'p.'; text = amount + 'p.';
} }
$orderPrice.html(text); $orderPrice.html(text);
$orderDates.text(moment(response.data.date_start).format('D MMM') + ' - ' + moment(response.data.date_end).format('D MMM')); $orderDates.text(moment(response.data.date_start).format('D MMM') + ' - ' + moment(response.data.date_end).format('D MMM'));
$('.but_btn_popup').attr('href', link+'?'+decodeURIComponent($.param({ $('.but_btn_popup').attr('href', link+'?'+decodeURIComponent($.param({
weekdays: weekdays, weekdays: weekdays,
date_start: moment(response.data.date_start).format('YYYY-MM-DD') date_start: moment(response.data.date_start).format('YYYY-MM-DD'),
use_bonuses: useBonuses || '',
}, true))); }, true)));
}).catch(() => {
$order.removeClass('order--loading');
}); });
} }
else { else {
$orderPrice.html('0p.'); $orderPrice.html('0p.');
$bonuses.hide();
} }
$('.order__days').html(daysText.length ? daysText.join(', ') : 'Ничего не выбрано');
} }
}); });

@ -1,14 +1,48 @@
import $ from 'jquery'; import $ from 'jquery';
import slugify from 'slugify';
$(document).ready(function () { export const main = () => {
// Обработчик выбора пола // Обработчик выбора пола
let genderInput = $('#gender') let genderInput = $('#gender')
$('div.js-select-option[data-gender-option]').on('click', function (e) { $('div.js-select-option[data-gender-option]').on('click', function (e) {
e.preventDefault(); e.preventDefault();
const currentGender = $(this).attr('data-gender'); const currentGender = $(this).attr('data-gender');
$('[data-gender]').removeClass('active'); $('[data-gender]').removeClass('active');
$(`[data-gender=${currentGender}]`).addClass('active'); $(`[data-gender=${currentGender}]`).addClass('active');
genderInput.val(currentGender) genderInput.val(currentGender)
}); });
})
$('#user-photo-upload').change(file => {
const input = file.target;
const reader = new FileReader();
reader.onload = () => {
const dataURL = reader.result;
const output = document.getElementById('photo');
output.src = dataURL;
};
reader.readAsDataURL(input.files[0]);
});
const $slug = $('#user-slug');
const changeSlug = () => {
const fName = $('#user-first-name').val();
const lName = $('#user-last-name').val();
$slug.val(slugify(`${fName} ${lName}`));
}
if(! $slug.data('current-slug')){
$('#user-first-name').change(changeSlug);
$('#user-last-name').change(changeSlug);
$slug.change(() => {
$('#user-first-name').unbind('change', changeSlug);
$('#user-last-name').unbind('change', changeSlug);
});
changeSlug();
}
$('#referrer-url').select().click(function(){
$(this).select();
});
}

@ -0,0 +1,5 @@
export const rupluralize = (value, args) => {
let digit = Math.trunc(value) + '';
digit = digit[digit.length - 1];
return value + ' ' + args[digit == '1' ? 0 : ('234'.search(digit) > -1 ? 1 : 2)];
}

@ -2167,6 +2167,10 @@ a.grey-link
width: 100px width: 100px
&__append &__append
text-transform: uppercase text-transform: uppercase
&__text
height: 36px
font-size: 18px
padding-top: 6px
&__input, &__input,
&__textarea &__textarea
width: 100% width: 100%
@ -3091,8 +3095,14 @@ a.grey-link
padding: 20px 20px 0 padding: 20px 20px 0
&__total &__total
margin-left: auto margin-left: auto
position: relative
del del
color: #a0a0a0 color: #a0a0a0
& .loading-loader
display: none
margin-left: -40px
&--loading .loading-loader
display: block
.lock .lock
padding: 50px 60px 40px padding: 50px 60px 40px
@ -3297,31 +3307,41 @@ a.grey-link
&__cell &__cell
padding: 0 10px padding: 0 10px
font-size: 13px font-size: 13px
&__success &__product
+fb
font-size: 12px
text-transform: uppercase
letter-spacing: 2px
flex: 0 0 60%
+m
flex: 0 0 75%
&__amount
flex: 0 0 20%
text-align: right
+m
flex: 0 0 25%
&__status
flex: 0 0 20%
text-align: right
color: $green-light
+m
flex: 0 0 100%
&_success
color: $green color: $green
&__pending &_pending
color: $gray color: $gray
&__error &_error
color: $pink color: $pink
&:first-child .transactions_bonuses .transactions
+fb &__product
font-size: 12px flex: 0 0 60%;
text-transform: uppercase +m
letter-spacing: 2px flex: 1 0 40%;
flex: 0 0 60% &__amount
+m flex: 0 0 10%;
flex: 0 0 75% &__user
&:nth-child(2), flex: 0 0 20%;
&:last-child
flex: 0 0 20%
text-align: right
&:nth-child(2)
+m
flex: 0 0 25%
&:last-child
color: $green-light
+m
flex: 0 0 100%
.empty .empty
max-width: 300px max-width: 300px

Loading…
Cancel
Save