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

remotes/origin/hotfix/LIL-691
gzbender 7 years ago
commit d56774ca70
  1. 55
      apps/payment/models.py
  2. 44
      apps/payment/views.py
  3. 1
      apps/school/templates/blocks/day_pay_btn.html
  4. 7
      apps/school/templates/blocks/schedule_item.html
  5. 2
      apps/school/templates/summer/day_pay_btn.html
  6. 20
      apps/school/views.py
  7. 10
      apps/user/views.py
  8. 2
      project/templates/blocks/messages.html
  9. 26
      project/templates/blocks/popup_buy.html
  10. 5
      project/utils.py
  11. 25
      project/views.py
  12. 5
      web/src/js/modules/popup.js
  13. 14
      web/src/sass/_common.sass

@ -1,5 +1,7 @@
from decimal import Decimal
import arrow
from django.db.models import Func, F
from paymentwall import Pingback
from polymorphic.models import PolymorphicModel
from polymorphic.managers import PolymorphicManager
@ -11,7 +13,7 @@ from django.core.validators import RegexValidator
from django.utils.timezone import now
from django.conf import settings
from project.utils import weekday_in_date_range
from project.utils import weekdays_in_date_range
from apps.course.models import Course
from apps.config.models import Config
@ -110,17 +112,14 @@ class Payment(PolymorphicModel):
ordering = ('created_at',)
@classmethod
def calc_amount(cls, payment=None, user=None, course=None, weekdays=None,
add_days=False, date_start=None, date_end=None):
def calc_amount(cls, payment=None, user=None, course=None, weekdays=None):
if isinstance(payment, CoursePayment):
course = payment.course
user = payment.user
if isinstance(payment, SchoolPayment):
user = payment.user
weekdays = payment.weekdays
add_days = payment.add_days
date_start = payment.date_start
date_end = payment.date_end
price = 0
referral_bonus = 0
referrer_bonus = 0
if hasattr(user, 'referral') and not user.referral.payment:
@ -130,29 +129,40 @@ class Payment(PolymorphicModel):
if course:
price = course.price
else:
aggregate = SchoolSchedule.objects.filter(
weekday__in=weekdays,
).aggregate(
models.Sum('month_price'),
)
if add_days:
_school_payment = SchoolPayment.objects.filter(
if user:
school_payments = SchoolPayment.objects.filter(
user=user,
date_start__lte=now().date(),
date_end__gte=now().date(),
add_days=False,
date_start__lte=date_start,
date_end__gte=date_end,
status__in=[
Pingback.PINGBACK_TYPE_REGULAR,
Pingback.PINGBACK_TYPE_GOODWILL,
Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED,
],
).last()
weekday_count = weekday_in_date_range(date_start, date_end, weekdays[0])
all_weekday_count = weekday_in_date_range(_school_payment.date_start, _school_payment.date_end,
weekdays[0])
price = Decimal(aggregate.get('month_price__sum', 0) * weekday_count // all_weekday_count)
)
school_schedules_purchased = school_payments.annotate(
joined_weekdays=Func(F('weekdays'), function='unnest', )
).values_list('joined_weekdays', flat=True).distinct()
weekdays = set(map(int, weekdays)) - set(school_schedules_purchased)
prev_school_payment = school_payments.last()
add_days = bool(prev_school_payment)
else:
price = Decimal(aggregate.get('month_price__sum', 0))
if price >= config.SERVICE_DISCOUNT_MIN_AMOUNT:
add_days = False
school_schedules = SchoolSchedule.objects.filter(
weekday__in=weekdays,
)
if add_days:
weekdays_count = weekdays_in_date_range(now().date(), prev_school_payment.date_end)
all_weekdays_count = weekdays_in_date_range(prev_school_payment.date_start, prev_school_payment.date_end)
for ss in school_schedules:
price += ss.month_price // all_weekdays_count.get(ss.weekday, 0) * weekdays_count.get(
ss.weekday, 0)
else:
price = school_schedules.aggregate(
models.Sum('month_price'),
).get('month_price__sum', 0)
if not (payment and payment.id) and price >= config.SERVICE_DISCOUNT_MIN_AMOUNT:
discount = config.SERVICE_DISCOUNT
amount = price - discount
referral_bonus = round(amount * referral_bonus / 100)
@ -218,6 +228,7 @@ class CoursePayment(Payment):
verbose_name_plural = 'Платежи за курсы'
class SchoolPayment(Payment):
weekdays = ArrayField(models.IntegerField(), size=7, verbose_name='Дни недели')
add_days = models.BooleanField('Докупленные дни', default=False)

@ -99,7 +99,6 @@ class SchoolBuyView(TemplateView):
host = str(host[0]) + '://' + str(host[1])
weekdays = set(request.GET.getlist('weekdays', []))
use_bonuses = request.GET.get('use_bonuses')
add_days = 'add_days' in request.GET
roistat_visit = request.COOKIES.get('roistat_visit', None)
if not weekdays:
messages.error(request, 'Выберите несколько дней недели.')
@ -109,21 +108,24 @@ class SchoolBuyView(TemplateView):
except ValueError:
messages.error(request, 'Ошибка выбора дней недели.')
return redirect('school:school')
if add_days:
_school_payment = SchoolPayment.objects.filter(
user=request.user,
date_start__lte=now().date(),
date_end__gte=now().date(),
add_days=False,
).first()
if not _school_payment:
add_days = False
prev_school_payment = SchoolPayment.objects.filter(
user=request.user,
date_start__lte=now().date(),
date_end__gte=now().date(),
add_days=False,
status__in=[
Pingback.PINGBACK_TYPE_REGULAR,
Pingback.PINGBACK_TYPE_GOODWILL,
Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED,
],
).first() # ??? first?
add_days = bool(prev_school_payment)
if add_days:
school_payment = SchoolPayment.objects.create(
user=request.user,
weekdays=weekdays,
date_start=now().date(),
date_end=_school_payment.date_end,
date_end=prev_school_payment.date_end,
add_days=True,
roistat_visit=roistat_visit,
)
@ -161,12 +163,13 @@ class SchoolBuyView(TemplateView):
@method_decorator(csrf_exempt, name='dispatch')
class PaymentwallCallbackView(View):
def add_months(self, sourcedate, months):
month = sourcedate.month - 1 + months
year = sourcedate.year + month // 12
month = month % 12 + 1
day = min(sourcedate.day, calendar.monthrange(year, month)[1])
return datetime.date(year, month, day)
def add_months(self, sourcedate, months=1):
result = arrow.get(sourcedate, settings.TIME_ZONE).shift(months=months)
if months == 1:
if (sourcedate.month == 2 and sourcedate.day >= 28) or (sourcedate.day == 31 and result.day <= 30)\
or (sourcedate.month == 1 and sourcedate.day >= 29 and result.day == 28):
result = result.replace(day=1, month=result.month + 1)
return result
def get_request_ip(self):
x_forwarded_for = self.request.META.get('HTTP_X_FORWARDED_FOR')
@ -179,7 +182,6 @@ class PaymentwallCallbackView(View):
def get(self, request, *args, **kwargs):
payment_raw_data = request.GET.copy()
pingback = Pingback(payment_raw_data, self.get_request_ip())
september2018 = datetime.date(2018, 9, 1)
if pingback.validate():
product_type_name, payment_id = pingback.get_product().get_id().split('_')
@ -232,12 +234,10 @@ class PaymentwallCallbackView(View):
date_end = school_payment.date_end
else:
date_start = arrow.get(school_payment.date_end, settings.TIME_ZONE).shift(days=1).datetime
date_end = arrow.get(date_start, settings.TIME_ZONE).shift(months=1).datetime
date_end = self.add_months(date_start)
else:
date_start = now().date()
if date_start < september2018:
date_start = september2018
date_end = arrow.get(date_start, settings.TIME_ZONE).shift(months=1, minutes=-1).datetime
date_end = self.add_months(date_start)
payment.date_start = date_start
payment.date_end = date_end
if product_type_name == 'course':

@ -2,5 +2,4 @@
data-popup=".js-popup-buy"
class="timing__btn btn"
data-day="{{ school_schedule.weekday }}"
href="{% url 'school-checkout' %}?weekdays={{ school_schedule.weekday }}&add_days=true"
>купить</a>

@ -4,9 +4,12 @@
<div class="timing__info">
<div class="timing__day{% if school_schedule.is_online %} active{% endif %}">
{{ school_schedule }}
{% if request.user_agent.is_mobile and school_schedule.trial_lesson %}
<a class="timing__trial-lesson js-video-modal" href="#" data-video-url="{{ school_schedule.trial_lesson }}">Пробный урок</a>
{% endif %}
</div>
{% if is_purchased and live_lesson %}
<div class="timing__date">{{ live_lesson.date|date:"j E" }}</div>
<div class="timing__date">{% if request.user_agent.is_mobile %}{{ live_lesson.date|date:"j b" }}{% else %}{{ live_lesson.date|date:"j E" }}{% endif %}</div>
{% endif %}
<div class="timing__time">{{ school_schedule.start_at }} (МСК)</div>
<div class="timing__buy">
@ -17,7 +20,7 @@
{% else %}
{% include './day_pay_btn.html' %}
{% endif %}
{% if school_schedule.trial_lesson %}
{% if not request.user_agent.is_mobile and school_schedule.trial_lesson %}
<a class="timing__trial-lesson js-video-modal" href="#" data-video-url="{{ school_schedule.trial_lesson }}">Пробный урок</a>
{% endif %}
</div>

@ -3,5 +3,5 @@
data-popup=".js-popup-auth"
{% endif %}
class="timing__btn btn"
href="{% url 'school-checkout' %}?weekdays={{ school_schedule.weekday }}&add_days=true"
href="{% url 'school-checkout' %}?weekdays={{ school_schedule.weekday }}"
>купить</a>

@ -167,27 +167,11 @@ class SchoolView(TemplateView):
)
school_payment_exists = school_payment.exists()
school_payment_future = SchoolPayment.objects.filter(
user=self.request.user,
status__in=[
Pingback.PINGBACK_TYPE_REGULAR,
Pingback.PINGBACK_TYPE_GOODWILL,
Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED,
],
date_start__gte=date_now,
date_end__gte=date_now
)
school_payment_exists_future = school_payment_future.exists()
school_purchased_future = school_payment_future.last()
school_schedules_purchased = school_payment.annotate(
joined_weekdays=Func(F('weekdays'), function='unnest',)
).values_list('joined_weekdays', flat=True).distinct()
else:
school_payment_exists = False
school_payment_exists_future = False
school_purchased_future = False
school_schedules_purchased = []
if all_schedules_purchased and is_previous:
live_lessons = LiveLesson.objects.filter(
@ -208,11 +192,11 @@ class SchoolView(TemplateView):
'is_previous': is_previous,
'course_items': Course.objects.filter(status=Course.PUBLISHED)[:6],
'is_purchased': school_payment_exists,
'is_purchased_future': school_payment_exists_future,
'is_purchased_future': False,
'min_school_price': SchoolSchedule.objects.aggregate(Min('month_price'))['month_price__min'],
'school_schedules': school_schedules,
'school_schedules_purchased': school_schedules_purchased,
'school_purchased_future': school_purchased_future,
'school_purchased_future': False,
'subscription_ends': school_payment.filter(add_days=False).first().date_end if school_payment_exists else None,
})
return context

@ -96,14 +96,8 @@ class ProfileView(TemplateView):
).all()
context['all_school_schedules'] = SchoolSchedule.objects.all()
school_payment_future = SchoolPayment.objects.filter(
user=self.object,
date_start__gte=now(),
date_end__gte=now()
)
context['is_purchased_future'] = school_payment_future.exists()
context['school_purchased_future'] = school_payment_future.last()
context['is_purchased_future'] = False
context['school_purchased_future'] = False
return context

@ -1,5 +1,5 @@
{% if messages %}
<div class="section section_gray section_menu">
<div class="section section_gray section_menu" style="margin-bottom: 20px;">
<div class="section__center center center_xs">
{% for message in messages %}
<div class="message message_{{ message.tags }}">{{ message }}</div>

@ -29,7 +29,10 @@
data-day="{{school_schedule.weekday}}"
data-price="{{school_schedule.month_price}}"
autocomplete="off"
{% if school_schedule.weekday in school_schedules_purchased or not is_purchased %}
{% if school_schedule.weekday in school_schedules_purchased %}
disabled
{% endif %}
{% if not is_purchased %}
checked
{% endif %}>
<span class="switch__content">
@ -38,8 +41,12 @@
<span class="switch__cell"></span>
<span class="switch__cell">{{ school_schedule.title }}</span>
<span class="buy__trial-lesson switch__cell">
{% if school_schedule.trial_lesson %}
<a class="js-video-modal" data-video-url="{{ school_schedule.trial_lesson }}" href="#">Пробный урок</a>
{% if school_schedule.weekday in school_schedules_purchased %}
Куплено
{% else %}
{% if school_schedule.trial_lesson %}
<a class="js-video-modal" data-video-url="{{ school_schedule.trial_lesson }}" href="">Пробный урок</a>
{% endif %}
{% endif %}
</span>
<span class="switch__cell">{{school_schedule.month_price}}р в мес.</span>
@ -55,7 +62,10 @@
data-day="{{school_schedule.weekday}}"
data-price="{{school_schedule.month_price}}"
autocomplete="off"
{% if school_schedule.weekday in school_schedules_purchased or not is_purchased %}
{% if school_schedule.weekday in school_schedules_purchased %}
disabled
{% endif %}
{% if not is_purchased %}
checked
{% endif %}>
<span class="switch__content">
@ -64,8 +74,12 @@
<span class="switch__cell"></span>
<span class="switch__cell">{{ school_schedule.title }}</span>
<span class="buy__trial-lesson switch__cell">
{% if school_schedule.trial_lesson %}
<a class="js-video-modal" data-video-url="{{ school_schedule.trial_lesson }}" href="">Пробный урок</a>
{% if school_schedule.weekday in school_schedules_purchased %}
Куплено
{% else %}
{% if school_schedule.trial_lesson %}
<a class="js-video-modal" data-video-url="{{ school_schedule.trial_lesson }}" href="">Пробный урок</a>
{% endif %}
{% endif %}
</span>
<span class="switch__cell">{{school_schedule.month_price}}р в мес.</span>

@ -13,6 +13,5 @@ def date_range(start, end):
return
def weekday_in_date_range(start, end, weekday):
counter = Counter([d.isoweekday() for d in date_range(start, end)])
return counter.get(weekday, 0)
def weekdays_in_date_range(start, end):
return Counter([d.isoweekday() for d in date_range(start, end)])

@ -5,6 +5,7 @@ from django.db.models import Min, Func, F
from django.contrib.auth import get_user_model
from django.views.generic import TemplateView
from django.utils.timezone import now
from paymentwall.pingback import Pingback
from apps.course.models import Course
from apps.school.models import SchoolSchedule
@ -49,26 +50,20 @@ class IndexView(TemplateView):
school_payment = SchoolPayment.objects.filter(
user=self.request.user,
date_start__lte=date_now,
date_end__gte=date_now
date_end__gte=date_now,
status__in=[
Pingback.PINGBACK_TYPE_REGULAR,
Pingback.PINGBACK_TYPE_GOODWILL,
Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED,
],
)
school_payment_exists = school_payment.exists()
school_schedules_purchased = school_payment.annotate(
joined_weekdays=Func(F('weekdays'), function='unnest',)
).values_list('joined_weekdays', flat=True).distinct()
school_payment_future = SchoolPayment.objects.filter(
user=self.request.user,
date_start__gte=date_now,
date_end__gte=date_now
)
school_payment_exists_future = school_payment_future.exists()
school_purchased_future = school_payment_future.last()
else:
school_payment_exists = False
school_payment_exists_future = False
school_purchased_future = False
school_schedules_purchased = []
if referrer and not self.request.user.is_authenticated:
@ -90,13 +85,13 @@ class IndexView(TemplateView):
'is_purchased': school_payment_exists,
'min_school_price': SchoolSchedule.objects.aggregate(Min('month_price'))['month_price__min'],
'school_schedules': SchoolSchedule.objects.all(),
'school_schedules_purchased': school_schedules_purchased,
'school_schedules_purchased': set(school_schedules_purchased),
'teachers': User.objects.filter(role=User.TEACHER_ROLE, show_in_mainpage=True),
'subscription_ends': school_payment.filter(add_days=False).first().date_end if school_payment_exists else None,
'subscription_ends_humanize': school_payment.filter(add_days=False).first().date_end_humanize if school_payment_exists else None,
'school_purchased_future': school_purchased_future,
'is_purchased_future': school_payment_exists_future,
'school_purchased_future': False,
'is_purchased_future': False,
})
return context

@ -92,6 +92,7 @@ $(document).ready(function () {
$(this).prop('checked', true);
});
}
updateCart();
});
@ -189,12 +190,8 @@ $(document).ready(function () {
}
$('.order__days').html((daysText.length) ? daysText.join(', '):'Ничего не выбрано');
var link = $('.but_btn_popup').data('link');
link = link+'?'+decodeURIComponent($.param({weekdays: weekdays, use_bonuses: useBonuses || ''}, true));
$('.but_btn_popup').attr('href', link);
}
updateCart();
});

@ -1836,6 +1836,8 @@ a.grey-link
transition: opacity .2s
&__cell
padding-right: 10px
+m
margin-bottom: 5px
&:first-child
padding-top: 3px
+fb
@ -3799,9 +3801,6 @@ a.grey-link
&__item.open &__buy,
&__item.open &__more
display: block
&__item.open &__buy
+m
display: flex
&__item.open &__cell
&:nth-child(3)
+m
@ -3863,7 +3862,7 @@ a.grey-link
&__date
opacity: .5
+m
margin: -3px 0 0 auto
margin: -3px 0 0 10px
&__buy
+m
flex: 1 0 0
@ -3872,13 +3871,14 @@ a.grey-link
display: inline-block
margin-top: 10px
+m
display: block
margin-bottom: -31px
position: absolute;
width: 120px;
margin-top: 6px;
&__time
margin: 15px 0
opacity: .5
+m
margin: -1px 15px
margin: -1px 15px 0 5px
font-size: 12px;
&__btn
margin-right: -60px

Loading…
Cancel
Save