LIL-653 Сделать пролонгацию подписки

remotes/origin/hotfix/LIL-691
gzbender 7 years ago
parent 534fb4467e
commit 1991d1b2b0
  1. 6
      api/v1/views.py
  2. 23
      apps/payment/models.py
  3. 46
      apps/payment/views.py
  4. 6
      apps/school/templates/summer/prolong_btn.html
  5. 6
      apps/school/templates/summer/schedule_purchased.html
  6. 5
      apps/school/views.py
  7. 16
      project/context_processors.py
  8. 2
      project/settings.py
  9. 37
      project/templates/blocks/popup_buy.html
  10. 59
      web/src/js/modules/comments_vue.js
  11. 68
      web/src/js/modules/popup.js

@ -1,3 +1,5 @@
from datetime import datetime
from django.contrib.auth import get_user_model
from rest_framework import status, views, viewsets, generics
@ -565,10 +567,12 @@ class PaymentViewSet(viewsets.ModelViewSet):
user = request.query_params.get('user')
course = request.query_params.get('course')
weekdays = request.query_params.getlist('weekdays[]')
date_start = request.query_params.get('date_start')
user = user and User.objects.get(pk=user)
course = course and Course.objects.get(pk=course)
date_start = date_start and datetime.strptime(date_start, '%Y-%m-%d')
return Response(Payment.calc_amount(user=user, course=course, weekdays=weekdays))
return Response(Payment.calc_amount(user=user, course=course, date_start=date_start, weekdays=weekdays))
class ContestViewSet(ExtendedModelViewSet):

@ -111,13 +111,25 @@ class Payment(PolymorphicModel):
ordering = ('created_at',)
@classmethod
def calc_amount(cls, course_payment=None, school_payment=None, user=None, course=None, weekdays=None):
def add_months(cls, 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.datetime
@classmethod
def calc_amount(cls, course_payment=None, school_payment=None, user=None, course=None, date_start=None, weekdays=None):
date_start = date_start or now().date()
date_end = Payment.add_months(date_start, 1)
if course_payment:
course = course_payment.course
user = course_payment.user
if school_payment:
user = school_payment.user
weekdays = school_payment.weekdays
date_start = school_payment.date_start
discount = 0
price = 0
if course:
@ -126,8 +138,8 @@ class Payment(PolymorphicModel):
if user:
school_payments = SchoolPayment.objects.filter(
user=user,
date_start__lte=now().date(),
date_end__gte=now().date(),
date_start__lte=date_start,
date_end__gte=date_start,
add_days=False,
status__in=[
Pingback.PINGBACK_TYPE_REGULAR,
@ -147,7 +159,8 @@ class Payment(PolymorphicModel):
weekday__in=weekdays,
)
if add_days:
weekdays_count = weekdays_in_date_range(now().date(), prev_school_payment.date_end)
date_end = prev_school_payment.date_end
weekdays_count = weekdays_in_date_range(date_start, 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(
@ -163,6 +176,8 @@ class Payment(PolymorphicModel):
'price': price,
'amount': amount,
'discount': discount,
'date_start': date_start,
'date_end': date_end,
}
def calc_commission(self):

@ -27,7 +27,7 @@ from apps.course.models import Course
from apps.school.models import SchoolSchedule
from apps.payment.tasks import transaction_to_mixpanel, product_payment_to_mixpanel, transaction_to_roistat
from .models import AuthorBalance, CoursePayment, SchoolPayment
from .models import AuthorBalance, CoursePayment, SchoolPayment, Payment
logger = logging.getLogger('django')
@ -101,6 +101,8 @@ class SchoolBuyView(TemplateView):
host = str(host[0]) + '://' + str(host[1])
weekdays = set(request.GET.getlist('weekdays', []))
roistat_visit = request.COOKIES.get('roistat_visit', None)
date_start = request.GET.get('date_start')
date_start = date_start and datetime.datetime.strptime(date_start, '%Y-%m-%d') or now().date()
if not weekdays:
messages.error(request, 'Выберите несколько дней недели.')
return redirect('school:summer-school')
@ -111,21 +113,21 @@ class SchoolBuyView(TemplateView):
return redirect('school:summer-school')
prev_school_payment = SchoolPayment.objects.filter(
user=request.user,
date_start__lte=now().date(),
date_end__gte=now().date(),
date_start__lte=date_start,
date_end__gte=date_start,
add_days=False,
status__in=[
Pingback.PINGBACK_TYPE_REGULAR,
Pingback.PINGBACK_TYPE_GOODWILL,
Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED,
],
).first() # ??? first?
).last()
add_days = bool(prev_school_payment)
if add_days:
school_payment = SchoolPayment.objects.create(
user=request.user,
weekdays=weekdays,
date_start=now().date(),
date_start=date_start,
date_end=prev_school_payment.date_end,
add_days=True,
roistat_visit=roistat_visit,
@ -138,6 +140,8 @@ class SchoolBuyView(TemplateView):
user=request.user,
weekdays=weekdays,
roistat_visit=roistat_visit,
date_start=date_start,
date_end=Payment.add_months(date_start),
)
product = Product(
f'school_{school_payment.id}',
@ -164,14 +168,6 @@ class SchoolBuyView(TemplateView):
@method_decorator(csrf_exempt, name='dispatch')
class PaymentwallCallbackView(View):
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.datetime
def get_request_ip(self):
x_forwarded_for = self.request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
@ -217,30 +213,6 @@ class PaymentwallCallbackView(View):
product_type_name,
)
if product_type_name == 'school':
school_payment = SchoolPayment.objects.filter(
user=payment.user,
add_days=False,
date_start__lte=now().date(),
date_end__gte=now().date(),
status__in=[
Pingback.PINGBACK_TYPE_REGULAR,
Pingback.PINGBACK_TYPE_GOODWILL,
Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED,
],
).last()
if school_payment:
if payment.add_days:
date_start = now().date()
date_end = school_payment.date_end
else:
date_start = arrow.get(school_payment.date_end, settings.TIME_ZONE).shift(days=1).datetime
date_end = self.add_months(date_start)
else:
date_start = now().date()
date_end = self.add_months(date_start)
payment.date_start = date_start
payment.date_end = date_end
if product_type_name == 'course':
properties = {
'payment_id': payment.id,

@ -1,9 +1,5 @@
<a
{% if not user.is_authenticated %}
data-popup=".js-popup-auth"
{% else %}
data-popup=".js-popup-buy"
{% endif %}
data-popup=".js-popup-buy" data-prolong="1" data-date-start="{{ prolong_date_start|date:'Y-m-d' }}"
class="casing__btn btn{% if pink %} btn_pink{% endif %}"
href="#"
>продлить</a>

@ -5,14 +5,10 @@
<div class="casing">
<div class="casing__col">
<div class="casing__subscribe">
{% if is_purchased %}
<div class="casing__msg">Подписка истекает
<span class="bold">{{ subscription_ends }}</span>
</div>
{% else %}
<div class="casing__msg">Подписка
<span class="bold">истекла</span>
</div>
{% if allow_prolong %}
{% include './prolong_btn.html' with pink=True %}
{% endif %}
</div>

@ -187,6 +187,7 @@ class SchoolView(TemplateView):
ll.school_schedule = school_schedules_dict.get(ll.date.isoweekday())
live_lessons_exists = live_lessons.exists()
live_lessons = live_lessons or None
subscription_ends = school_payment.filter(add_days=False).last().date_end if school_payment_exists else None
context.update({
'online': online,
'live_lessons': live_lessons,
@ -199,7 +200,9 @@ class SchoolView(TemplateView):
'school_schedules': school_schedules,
'school_schedules_purchased': school_schedules_purchased,
'school_purchased_future': False,
'subscription_ends': school_payment.filter(add_days=False).first().date_end if school_payment_exists else None,
'subscription_ends': subscription_ends,
'prolong_date_start': subscription_ends + timedelta(days=1),
'allow_prolong': subscription_ends - date_now <= timedelta(days=14)
})
return context

@ -1,3 +1,4 @@
from django.db.models import Func, F
from django.utils.timezone import now
from paymentwall.pingback import Pingback
@ -14,7 +15,7 @@ def baner(request):
return {'baner': Baner.objects.filter(use=True).first()}
def is_summer_school_purchased(request):
def school_purchased(request):
if request.user.is_authenticated:
n = now().date()
school_payment = SchoolPayment.objects.filter(
@ -27,5 +28,14 @@ def is_summer_school_purchased(request):
date_start__lte=n,
date_end__gte=n
)
return {'is_summer_school_purchased': school_payment.exists()}
return {'is_summer_school_purchased': False}
school_schedules_purchased = school_payment.annotate(
joined_weekdays=Func(F('weekdays'), function='unnest', )
).values_list('joined_weekdays', flat=True).distinct()
return {
'is_summer_school_purchased': school_payment.exists(),
'school_schedules_purchased': school_schedules_purchased,
}
return {
'is_school_purchased': False,
'school_schedules_purchased': [],
}

@ -94,7 +94,7 @@ TEMPLATES = [
'context_processors': [
'project.context_processors.config',
'project.context_processors.baner',
'project.context_processors.is_summer_school_purchased',
'project.context_processors.school_purchased',
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',

@ -20,40 +20,6 @@
</div>
<div class="buy__col">
<div class="buy__list">
{% if all_school_schedules %}
{% for school_schedule in all_school_schedules %}
<label class="switch switch_lesson">
<input
class="switch__input"
type="checkbox"
data-day="{{school_schedule.weekday}}"
data-price="{{school_schedule.month_price}}"
autocomplete="off"
{% if school_schedule.weekday in school_schedules_purchased %}
disabled
{% endif %}
{% if not is_purchased %}
checked
{% endif %}>
<span class="switch__content">
<span class="switch__cell">{{ school_schedule }}</span>
{% comment %} dont delete {% endcomment %}
<span class="switch__cell"></span>
<span class="switch__cell">{{ school_schedule.title }}</span>
<span class="buy__trial-lesson switch__cell">
{% 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>
</span>
</label>
{% endfor %}
{% else %}
{% for school_schedule in school_schedules %}
<label class="switch switch_lesson">
<input
@ -63,6 +29,7 @@
data-price="{{school_schedule.month_price}}"
autocomplete="off"
{% if school_schedule.weekday in school_schedules_purchased %}
data-purchased="1"
disabled
{% endif %}
{% if not is_purchased %}
@ -86,7 +53,6 @@
</span>
</label>
{% endfor %}
{% endif %}
</div>
</div>
<div class="buy__col">
@ -102,6 +68,7 @@
</div>
<div class="order__info">
<div class="order__label">ШКОЛА</div>
<div class="order__dates"></div>
<div class="order__days"></div>
</div>
<div class="order__foot">

@ -1,59 +0,0 @@
import Vue from 'vue';
import Comments from '../../components/Comments';
//import $ from 'jquery';
if (process.env.NODE_ENV === 'development') {
// Enable vue-devtools
Vue.config.devtools = true;
}
let app = new Vue({
el: '#comments_block',
data(){
return {
userId: 123,
accessToken: 123,
comments: [{
author: {
get_full_name: 'John Doe',
photo: {url: ''},
},
created_at_humanize: '12 07 18',
content: 'content content content content',
id: 1,
is_child_node: false
}, {
author: {
get_full_name: 'Sarah Conor',
photo: {url: ''},
},
created_at_humanize: '5 05 18',
content: 'hasta la vista',
id: 2,
is_child_node: false,
children: [{
author: {
get_full_name: 'John Doe',
photo: {url: ''},
},
created_at_humanize: '12 07 18',
content: 'content content content content',
id: 10,
is_child_node: true
}, {
author: {
get_full_name: 'Sarah Conor',
photo: {url: ''},
},
created_at_humanize: '5 05 18',
content: 'hasta la vista',
id: 20,
is_child_node: true,
}]
}]
}
},
components: {
'comments': Comments,
}
});

@ -1,6 +1,9 @@
import $ from 'jquery';
import moment from 'moment';
import {api} from './api';
moment.locale('ru');
var selectedWeekdays = {};
$(document).ready(function () {
$(".js-video-modal").each(function(){
@ -46,21 +49,12 @@ $(document).ready(function () {
popup = $(data);
showPopup();
let is_extend = false;
if(data === '.js-popup-buy') {
console.log('reset selected');
popup.data('date-start', $this.data('date-start') || '');
popup.data('day', $this.data('day') || '');
$('[data-day]').prop('checked', false);
if ($this.text() === 'продлить') {
//data-purchased
//restore purchased selection
console.log('restore purchased');
$('[data-purchased]').each(function(){
$('[data-day='+$(this).data('purchased')+']').prop('checked', true);
});
is_extend = true;
}
if(! window.LIL_STORE.user.id) {
const $btn = popup.find('.buy__btn');
$btn.click(function(event) {
@ -72,7 +66,30 @@ $(document).ready(function () {
});
});
}
if ($this.data('prolong')) {
$('[data-day][data-purchased]').each(function(){
$(this).prop('checked', true).prop('disabled', false);
});
}
else{
if($this.data('day')) {
let day = $this.data('day');
$('[data-day][data-purchased]').each(function(){
$(this).prop('checked', false).prop('disabled', true);
});
$('[data-day='+day+']').prop('checked', true);
}
else{
$('[data-day]').each(function(){
$(this).prop('checked', true).prop('disabled', false);
});
}
}
updateCart();
}
if( data === '.js-popup-auth') {
let nextUrl = $this.data('auth-next-url');
if(nextUrl === 'href') {
@ -80,20 +97,6 @@ $(document).ready(function () {
}
popup.data('next-url', nextUrl);
}
if($this.data('day')) {
let day = $this.data('day');
$('[data-day='+day+']').prop('checked', true);
}
if(!is_extend && !$this.data('day')) {
console.log('check all');
$('[data-day]').each(function(){
$(this).prop('checked', true);
});
}
updateCart();
});
$('.js-popup-close').on('click', function(e){
@ -147,7 +150,10 @@ $(document).ready(function () {
});
function updateCart(){
var link = $('.but_btn_popup').data('link');
var $orderPrice = $('.order_price_text');
var $orderDates = $('.order__dates');
var dateStart = popup.data('date-start');
var days = ['', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота', 'Воскресенье'];
var weekdays = [];
var daysText = [];
@ -160,7 +166,7 @@ $(document).ready(function () {
});
if(weekdays.length){
api.getPaymentAmount({ user: window.LIL_STORE.user.id, weekdays: weekdays })
api.getPaymentAmount({ user: window.LIL_STORE.user.id, weekdays: weekdays, date_start: dateStart})
.then((response) => {
var text = '';
if(response.data.price != response.data.amount) {
@ -169,16 +175,16 @@ $(document).ready(function () {
text = response.data.amount+'p.';
}
$orderPrice.html(text);
$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({
weekdays: weekdays,
date_start: moment(response.data.date_start).format('YYYY-MM-DD')
}, true)));
});
}
else {
$orderPrice.html('0p.');
}
$('.order__days').html(daysText.length ? daysText.join(', ') : 'Ничего не выбрано');
var link = $('.but_btn_popup').data('link');
link = link+'?'+decodeURIComponent($.param({weekdays: weekdays}, true));
$('.but_btn_popup').attr('href', link);
}
});

Loading…
Cancel
Save