diff --git a/api/v1/views.py b/api/v1/views.py index 2389a4db..7620244b 100644 --- a/api/v1/views.py +++ b/api/v1/views.py @@ -1,7 +1,7 @@ from django.contrib.auth import get_user_model from rest_framework import status, views, viewsets, generics -from rest_framework.decorators import detail_route, list_route +from rest_framework.decorators import detail_route, list_route, action from rest_framework.response import Response from . import ExtendedModelViewSet @@ -476,6 +476,17 @@ class PaymentViewSet(ExtendedModelViewSet): return queryset.filter(status__isnull=False).order_by('-created_at') + @action(methods=['get'], detail=False, url_path='calc-amount', authentication_classes=[], permission_classes=[]) + def calc_amount(self, request, pk=None): + user = request.query_params.get('user') + course = request.query_params.get('course') + weekdays = request.query_params.getlist('weekdays[]') + user = user and User.objects.get(pk=user) + course = course and Course.objects.get(pk=course) + + return Response(Payment.calc_amount(user=user, course=course, weekdays=weekdays)) + + class ContestViewSet(ExtendedModelViewSet): queryset = Contest.objects.all() serializer_class = ContestCreateSerializer diff --git a/apps/payment/models.py b/apps/payment/models.py index 25e98c82..4645b68d 100644 --- a/apps/payment/models.py +++ b/apps/payment/models.py @@ -1,4 +1,5 @@ import arrow +from django.db.models import Func, F from paymentwall import Pingback from polymorphic.models import PolymorphicModel @@ -11,7 +12,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 @@ -109,6 +110,58 @@ class Payment(PolymorphicModel): verbose_name_plural = 'Платежи' ordering = ('created_at',) + @classmethod + def calc_amount(cls, course_payment=None, school_payment=None, user=None, course=None, weekdays=None): + if course_payment: + course = course_payment.course + user = course_payment.user + if school_payment: + user = school_payment.user + weekdays = school_payment.weekdays + discount = 0 + price = 0 + if course: + price = course.price + else: + school_payments = SchoolPayment.objects.filter( + user=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, + ], + ) + 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) + school_schedules = SchoolSchedule.objects.filter( + weekday__in=weekdays, + ) + prev_school_payment = school_payments.last() + add_days = bool(prev_school_payment) + 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 (school_payment and school_payment.id) and price >= config.SERVICE_DISCOUNT_MIN_AMOUNT: + discount = config.SERVICE_DISCOUNT + amount = price - discount + return { + 'price': price, + 'amount': amount, + 'discount': discount, + } + def calc_commission(self): return self.amount * config.SERVICE_COMMISSION / 100 @@ -137,7 +190,8 @@ class CoursePayment(Payment): verbose_name_plural = 'Платежи за курсы' def save(self, *args, **kwargs): - self.amount = self.course.price + 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: @@ -169,32 +223,8 @@ class SchoolPayment(Payment): return days def save(self, *args, **kwargs): - aggregate = SchoolSchedule.objects.filter( - weekday__in=self.weekdays, - ).aggregate( - models.Sum('month_price'), - ) - if self.add_days: - _school_payment = SchoolPayment.objects.filter( - add_days=False, - date_start__lte=self.date_start, - date_end__gte=self.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(self.date_start, self.date_end, self.weekdays[0]) - all_weekday_count = weekday_in_date_range(_school_payment.date_start, _school_payment.date_end, self.weekdays[0]) - month_price_sum = aggregate.get('month_price__sum', 0) * weekday_count // all_weekday_count - else: - month_price_sum = aggregate.get('month_price__sum', 0) - if self.id is None and month_price_sum >= config.SERVICE_DISCOUNT_MIN_AMOUNT: - discount = config.SERVICE_DISCOUNT - else: - discount = 0 - self.amount = month_price_sum - discount + amount_data = Payment.calc_amount(school_payment=self) + self.amount = amount_data.get('amount') super().save(*args, **kwargs) @property diff --git a/apps/payment/views.py b/apps/payment/views.py index 1c705609..4137b74b 100644 --- a/apps/payment/views.py +++ b/apps/payment/views.py @@ -98,7 +98,6 @@ class SchoolBuyView(TemplateView): host = urlsplit(self.request.META.get('HTTP_REFERER')) host = str(host[0]) + '://' + str(host[1]) weekdays = set(request.GET.getlist('weekdays', [])) - add_days = 'add_days' in request.GET roistat_visit = request.COOKIES.get('roistat_visit', None) if not weekdays: messages.error(request, 'Выберите несколько дней недели.') @@ -108,27 +107,30 @@ class SchoolBuyView(TemplateView): except ValueError: messages.error(request, 'Ошибка выбора дней недели.') return redirect('school:summer-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, ) if school_payment.amount <= 0: messages.error(request, 'Выбранные дни отсутствуют в оставшемся периоде подписки') - return redirect(reverse_lazy('school:summer-school')) + return redirect(reverse_lazy('school:school')) else: school_payment = SchoolPayment.objects.create( user=request.user, diff --git a/apps/school/templates/blocks/day_pay_btn.html b/apps/school/templates/blocks/day_pay_btn.html index 9f90c17d..ec00046f 100644 --- a/apps/school/templates/blocks/day_pay_btn.html +++ b/apps/school/templates/blocks/day_pay_btn.html @@ -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" >купить diff --git a/apps/school/templates/summer/day_pay_btn.html b/apps/school/templates/summer/day_pay_btn.html index 155b8827..f44bee84 100644 --- a/apps/school/templates/summer/day_pay_btn.html +++ b/apps/school/templates/summer/day_pay_btn.html @@ -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 }}" >купить diff --git a/project/templates/blocks/messages.html b/project/templates/blocks/messages.html index 2f63fd3d..9702c39d 100644 --- a/project/templates/blocks/messages.html +++ b/project/templates/blocks/messages.html @@ -1,5 +1,5 @@ {% if messages %} -