diff --git a/apps/payment/models.py b/apps/payment/models.py index 81acf8e7..c200dc26 100644 --- a/apps/payment/models.py +++ b/apps/payment/models.py @@ -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) diff --git a/apps/payment/views.py b/apps/payment/views.py index 8b1c2544..04e72f5d 100644 --- a/apps/payment/views.py +++ b/apps/payment/views.py @@ -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': 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/blocks/schedule_item.html b/apps/school/templates/blocks/schedule_item.html index 63dde442..7e860a4e 100644 --- a/apps/school/templates/blocks/schedule_item.html +++ b/apps/school/templates/blocks/schedule_item.html @@ -4,9 +4,12 @@