From e4a148d383179bad9ceafdb333d673d763cb5d5b Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 12 Apr 2018 16:29:20 +0300 Subject: [PATCH 01/25] finance logging --- finance/migrations/0010_auto_20180412_1628.py | 40 +++++++++++++++++++ finance/models.py | 21 +++++++++- finance/tasks.py | 21 +++++----- finance/views.py | 13 +++--- 4 files changed, 77 insertions(+), 18 deletions(-) create mode 100644 finance/migrations/0010_auto_20180412_1628.py diff --git a/finance/migrations/0010_auto_20180412_1628.py b/finance/migrations/0010_auto_20180412_1628.py new file mode 100644 index 0000000..ef554ce --- /dev/null +++ b/finance/migrations/0010_auto_20180412_1628.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2018-04-12 16:28 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('finance', '0009_invoicerebilling_pay_count'), + ] + + operations = [ + migrations.RemoveField( + model_name='invoicerebilling', + name='pay_count', + ), + migrations.AddField( + model_name='bill', + name='freeze', + field=models.BooleanField(default=False, verbose_name='Отказ от платежей'), + ), + migrations.AddField( + model_name='invoice', + name='date_of_payment', + field=models.DateTimeField(blank=True, null=True, verbose_name='Дата фактической оплаты'), + ), + migrations.AddField( + model_name='invoice', + name='expected_date', + field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Ожидаемая дата платежа'), + ), + migrations.AlterField( + model_name='invoice', + name='date', + field=models.DateTimeField(auto_now_add=True, verbose_name='Дата создания платежа'), + ), + ] diff --git a/finance/models.py b/finance/models.py index 434e562..98d749c 100755 --- a/finance/models.py +++ b/finance/models.py @@ -1,7 +1,10 @@ # coding=utf-8 +from dateutil.relativedelta import relativedelta + from django.conf import settings from django.core.mail import EmailMessage from django.db import models +from django.utils import timezone from yandex_money.models import Payment @@ -13,6 +16,7 @@ class Bill(models.Model): blank=True, editable=False) description = models.TextField(verbose_name='Внутренняя заметка', blank=True) date = models.DateTimeField(verbose_name="Дата выставления", auto_now_add=True) + freeze = models.BooleanField(verbose_name='Отказ от платежей', default=False) def __str__(self): return '%s: %s' % (self.id, self.user) @@ -59,7 +63,9 @@ class Invoice(models.Model): blank=True, editable=False) bill = models.ForeignKey(to=Bill, verbose_name="Связный счёт") is_open = models.BooleanField(default=True, verbose_name="Открывает ли платёж курс") - date = models.DateTimeField(auto_now_add=True) + date = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания платежа") + expected_date = models.DateTimeField(default=timezone.now, verbose_name="Ожидаемая дата платежа") + date_of_payment = models.DateTimeField(verbose_name="Дата фактической оплаты", blank=True, null=True) def get_comment(self): return '''Вам выставлен счёт,''' if \ @@ -86,7 +92,18 @@ class Invoice(models.Model): class InvoiceRebilling(Invoice): rebilling_on = models.BooleanField(verbose_name='Повторять платеж', default=False, editable=False) - pay_count = models.SmallIntegerField(verbose_name='Всего платежей', editable=False) + + def create_child_pays(self, count): + for idx in range(count-1): + InvoiceRebilling.objects.create( + bill=self.bill, + comment=self.comment, + method=self.method, + status=self.status, + is_open=self.is_open, + rebilling_on=False, + expected_date=(timezone.now() + relativedelta(months=idx+1)), + ) class Meta: verbose_name = 'Повторный платёж' diff --git a/finance/tasks.py b/finance/tasks.py index a28b3ae..b7912e4 100644 --- a/finance/tasks.py +++ b/finance/tasks.py @@ -8,7 +8,7 @@ import requests from django_celery_beat.models import CrontabSchedule, PeriodicTask from yandex_money.models import Payment -from finance.models import Invoice +from finance.models import InvoiceRebilling from lms import celery_app from django.conf import settings @@ -19,12 +19,11 @@ logger_yandex = logging.getLogger('yandex_money') def setup_periodic_billing(order_number): # TODO: настроить периодичность и срок окончания # 12:00 первого числа каждого месяца + logger_yandex.info("Оформдение новой рассрочки") + schedule, _ = CrontabSchedule.objects.get_or_create( minute='0', - hour='12', - day_of_week='*', - day_of_month='1', - month_of_year='*' + hour='*/3', ) PeriodicTask.objects.create( crontab=schedule, @@ -40,18 +39,18 @@ def setup_periodic_billing(order_number): @celery_app.task def periodic_billing(order_number): try: - sample = Invoice.objects.get(yandex_pay__order_number=order_number) - except Invoice.DoesNotExist: + sample = InvoiceRebilling.objects.get(yandex_pay__order_number=order_number) + except InvoiceRebilling.DoesNotExist: raise ValueError('Номер заказа {} не найден'.format(order_number)) bill = sample.bill - invoice = Invoice.objects.create( + invoice = InvoiceRebilling.objects.create( status='P', price=sample.price, method=sample.method, - rebilling=True, - bill=bill + is_open=sample.is_open, + bill=bill, ) if invoice.method == 'Y': @@ -66,7 +65,7 @@ def periodic_billing(order_number): invoice.yandex_pay = yandex_pay invoice.save() - repeat_card_payment(invoice) + repeat_card_payment(invoice) def repeat_card_payment(invoice): diff --git a/finance/views.py b/finance/views.py index 3a79e0d..313228d 100644 --- a/finance/views.py +++ b/finance/views.py @@ -135,8 +135,10 @@ class InvoiceDetailView(APIView): method=method, status=status, is_open=is_open, - pay_count=pay_count, + rebilling_on=True, ) + invoice.create_child_pays(pay_count) + else: try: invoice = Invoice.objects.get(id=invoice_id) @@ -409,10 +411,13 @@ class YandexAvisoView(APIView): @staticmethod def post(request): data = dict() + rebilling = None for i in request.body.decode('utf-8').split('&'): key = i.split('=')[0] val = i.split('=')[1] data[key] = val + if key == 'rebillingOn': + rebilling = val try: pay = Payment.objects.get(order_number=data['orderNumber']) @@ -457,11 +462,9 @@ class YandexAvisoView(APIView): msg.attach_alternative(html_content, "text/html") msg.send() - try: + if rebilling: InvoiceRebilling.objects.get(yandex_pay=pay) setup_periodic_billing(pay.order_number) - except InvoiceRebilling.DoesNotExist: - pass return HttpResponse(xml_res, content_type='application/xml') @@ -470,7 +473,7 @@ class YandexFailView(APIView): renderer_classes = (JSONRenderer,) @staticmethod - def post(request): + def get(request): data = dict() for i in request.body.decode('utf-8').split('&'): key = i.split('=')[0] From 94d9cfb4607bca52be751755be178c388958b107 Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 12 Apr 2018 17:10:19 +0300 Subject: [PATCH 02/25] finance logging --- finance/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finance/models.py b/finance/models.py index 98d749c..5849def 100755 --- a/finance/models.py +++ b/finance/models.py @@ -94,7 +94,7 @@ class InvoiceRebilling(Invoice): rebilling_on = models.BooleanField(verbose_name='Повторять платеж', default=False, editable=False) def create_child_pays(self, count): - for idx in range(count-1): + for idx in range(int(count)-1): InvoiceRebilling.objects.create( bill=self.bill, comment=self.comment, From aa6dca71382eb6d8b351adb9dd034c91003e7d0e Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 12 Apr 2018 17:14:30 +0300 Subject: [PATCH 03/25] finance logging --- finance/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/finance/models.py b/finance/models.py index 5849def..8014373 100755 --- a/finance/models.py +++ b/finance/models.py @@ -99,8 +99,9 @@ class InvoiceRebilling(Invoice): bill=self.bill, comment=self.comment, method=self.method, - status=self.status, + status='W', is_open=self.is_open, + price=self.price, rebilling_on=False, expected_date=(timezone.now() + relativedelta(months=idx+1)), ) From 2ab0a525bbf1b8c6b4e3066d3f958da655b6286c Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 12 Apr 2018 17:25:55 +0300 Subject: [PATCH 04/25] finance logging --- finance/views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/finance/views.py b/finance/views.py index 313228d..47d7ef9 100644 --- a/finance/views.py +++ b/finance/views.py @@ -109,8 +109,8 @@ class InvoiceDetailView(APIView): price = request.JSON.get('price', None) comment = request.JSON.get('comment', None) real_price = request.JSON.get('real_price', None) - rebilling_on = request.JSON.get('is_rebilling', False) - pay_count = request.JSON.get('pay_count', None) + # rebilling_on = request.JSON.get('is_rebilling', False) + pay_count = int(request.JSON.get('pay_count', 0)) + 1 if bill_id is None: return Response("Не передан id счёта", status=400) @@ -129,7 +129,7 @@ class InvoiceDetailView(APIView): if bill.check_validate(invoice_id) and is_open: return Response("Уже есть платёж открывающий курс", status=400) - if rebilling_on: + if pay_count > 1: invoice = InvoiceRebilling.objects.create( bill=bill, method=method, From 87d37636e5cc9fb474cfd3150fa6ac2c6053a1fa Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 12 Apr 2018 17:34:24 +0300 Subject: [PATCH 05/25] finance logging --- finance/views.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/finance/views.py b/finance/views.py index 47d7ef9..6523f36 100644 --- a/finance/views.py +++ b/finance/views.py @@ -109,8 +109,7 @@ class InvoiceDetailView(APIView): price = request.JSON.get('price', None) comment = request.JSON.get('comment', None) real_price = request.JSON.get('real_price', None) - # rebilling_on = request.JSON.get('is_rebilling', False) - pay_count = int(request.JSON.get('pay_count', 0)) + 1 + pay_count = int(request.JSON.get('pay_count', '1')) if bill_id is None: return Response("Не передан id счёта", status=400) @@ -137,7 +136,6 @@ class InvoiceDetailView(APIView): is_open=is_open, rebilling_on=True, ) - invoice.create_child_pays(pay_count) else: try: @@ -174,6 +172,9 @@ class InvoiceDetailView(APIView): invoice.is_open = is_open invoice.comment = comment + if pay_count > 1: + invoice.create_child_pays(pay_count) + if invoice.method == 'Y' and invoice.yandex_pay is None: yandex_pay = Payment.objects.create( order_amount=invoice.price, From 57678227b78cf91b545219d278d3a6911f167ff1 Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 12 Apr 2018 18:07:23 +0300 Subject: [PATCH 06/25] finance logging --- finance/urls.py | 2 ++ finance/views.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/finance/urls.py b/finance/urls.py index 7a83d74..7cb09a1 100644 --- a/finance/urls.py +++ b/finance/urls.py @@ -3,6 +3,8 @@ from finance import views urlpatterns = [ url(r'payment/([0-9]{1,99})/$', views.YandexPay.as_view()), + url(r'bill/([0-9]{1,99})/freeze/$', views.FreezeView.as_view()), + url(r'bill/([0-9]{1,99})/unfreeze/$', views.UnFreezeView.as_view()), url(r'bills/$', views.BillListView.as_view()), url(r'bills/([0-9]{1,99})/$', views.BillDetailView.as_view()), url(r'bills_find/$', views.FindBillView.as_view()), diff --git a/finance/views.py b/finance/views.py index 6523f36..5cf5c8e 100644 --- a/finance/views.py +++ b/finance/views.py @@ -39,6 +39,38 @@ def test_pay(request): }) +class UnFreezeView(APIView): + renderer_classes = (JSONRenderer,) + + @staticmethod + def post(request, id): + if request.user.is_authenticated: + try: + bill = Bill.objects.get(id=id) + bill.freeze = False + bill.save() + except Bill.DoesNotExist: + return Response("Счёт не найден", status=404) + return Response(status=204) + return Response("Permission denied", status=403) + + +class FreezeView(APIView): + renderer_classes = (JSONRenderer,) + + @staticmethod + def post(request, id): + if request.user.is_authenticated: + try: + bill = Bill.objects.get(id=id) + bill.freeze = True + bill.save() + except Bill.DoesNotExist: + return Response("Счёт не найден", status=404) + return Response(status=204) + return Response("Permission denied", status=403) + + class BillListView(APIView): renderer_classes = (JSONRenderer,) From 78271b917605a6f5755d748220c71c0c65bac51c Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 13 Apr 2018 13:02:15 +0300 Subject: [PATCH 07/25] finance logging --- finance/models.py | 13 +++++++++++++ finance/views.py | 17 ++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/finance/models.py b/finance/models.py index 8014373..e63f55f 100755 --- a/finance/models.py +++ b/finance/models.py @@ -7,6 +7,8 @@ from django.db import models from django.utils import timezone from yandex_money.models import Payment +from progress.models import Progress + class Bill(models.Model): course_token = models.UUIDField(verbose_name="Токен курса", editable=False) @@ -21,6 +23,17 @@ class Bill(models.Model): def __str__(self): return '%s: %s' % (self.id, self.user) + def freeze_course(self, user): + try: + p = Progress.objects.get(user=user, course_token=self.course_token) + p.freeze = True + p.save() + except Progress.DoesNotExist: + pass + + self.freeze = True + self.save() + def get_full_price(self): return sum([i.price for i in self.invoice_set.all() if not i.price is None]) diff --git a/finance/views.py b/finance/views.py index 5cf5c8e..bb4e472 100644 --- a/finance/views.py +++ b/finance/views.py @@ -39,31 +39,30 @@ def test_pay(request): }) -class UnFreezeView(APIView): +class FreezeView(APIView): renderer_classes = (JSONRenderer,) @staticmethod - def post(request, id): + def post(request, pk): if request.user.is_authenticated: try: - bill = Bill.objects.get(id=id) - bill.freeze = False - bill.save() + bill = Bill.objects.get(id=pk) + bill.freeze_course(request.user) except Bill.DoesNotExist: return Response("Счёт не найден", status=404) return Response(status=204) return Response("Permission denied", status=403) -class FreezeView(APIView): +class UnFreezeView(APIView): renderer_classes = (JSONRenderer,) @staticmethod - def post(request, id): + def post(request, pk): if request.user.is_authenticated: try: - bill = Bill.objects.get(id=id) - bill.freeze = True + bill = Bill.objects.get(id=pk) + bill.freeze = False bill.save() except Bill.DoesNotExist: return Response("Счёт не найден", status=404) From 081353eed15d8ecee0d13358b978933176637cbb Mon Sep 17 00:00:00 2001 From: Andrey Date: Sat, 14 Apr 2018 15:11:16 +0300 Subject: [PATCH 08/25] finance logging --- access/views.py | 1 + courses/models.py | 5 +++-- finance/models.py | 29 ++++++++++++++++++++--------- lms/settings.py | 10 ++++++++++ progress/views.py | 1 + 5 files changed, 35 insertions(+), 11 deletions(-) diff --git a/access/views.py b/access/views.py index c3e69dd..8cb6305 100644 --- a/access/views.py +++ b/access/views.py @@ -128,6 +128,7 @@ class FindUserView(APIView): class DetailUserView(APIView): renderer_classes = (JSONRenderer,) + permission_classes = (permissions.IsAuthenticated,) @staticmethod def post(request, out_key=None): diff --git a/courses/models.py b/courses/models.py index ce50bf7..eacc7e6 100755 --- a/courses/models.py +++ b/courses/models.py @@ -72,7 +72,7 @@ class Topic(models.Model): class CourseManager(models.Manager): - def update_or_create_course(self, image=None, big_image=None, statistic=None, + def update_or_create_course(self, image=None, big_image=None, statistic=None, old_slug=None, big_mobile_image=None, slug=None, teacher_tokens=None, level=None, direction=None, **kwargs): @@ -102,10 +102,11 @@ class CourseManager(models.Manager): kwargs['direction'] = get_real_name(COURSE_DIRECTION, direction[0]) try: - course = self.get(slug=slug) + course = self.get(slug=old_slug) for i in kwargs: if kwargs[i]: setattr(course, i, kwargs[i]) + course.slug = slug course.save() except ObjectDoesNotExist: diff --git a/finance/models.py b/finance/models.py index e63f55f..47ac532 100755 --- a/finance/models.py +++ b/finance/models.py @@ -6,10 +6,14 @@ from django.core.mail import EmailMessage from django.db import models from django.utils import timezone from yandex_money.models import Payment +import logging from progress.models import Progress +logger_business_rules = logging.getLogger('business_rules') + + class Bill(models.Model): course_token = models.UUIDField(verbose_name="Токен курса", editable=False) user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='Плательщик', related_name='bill_user') @@ -24,15 +28,22 @@ class Bill(models.Model): return '%s: %s' % (self.id, self.user) def freeze_course(self, user): - try: - p = Progress.objects.get(user=user, course_token=self.course_token) - p.freeze = True - p.save() - except Progress.DoesNotExist: - pass - - self.freeze = True - self.save() + if self.invoice_set.exclude('F').exists(): + try: + p = Progress.objects.get(user=user, course_token=self.course_token) + p.freeze = True + p.save() + except Progress.DoesNotExist: + pass + + self.freeze = True + self.save() + + else: + logger_business_rules.warning('Попытка нарушения правила отказа от плотежей', exc_info=True, extra={ + 'description': 'Пользоваль пытается отказатся от платежей по счёту, однако все платежи уже оплачены', + 'user': user, + }) def get_full_price(self): return sum([i.price for i in self.invoice_set.all() if not i.price is None]) diff --git a/lms/settings.py b/lms/settings.py index c9f4e61..ce5acd6 100644 --- a/lms/settings.py +++ b/lms/settings.py @@ -212,6 +212,11 @@ LOGGING = { 'class': 'raven.contrib.django.raven_compat.handlers.SentryHandler', 'tags': {'custom-tag': 'yandex'}, }, + 'business_rules': { + 'level': 'DEBUG', + 'class': 'raven.contrib.django.raven_compat.handlers.SentryHandler', + 'tags': {'custom-tag': 'business_rules'}, + }, 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', @@ -228,6 +233,11 @@ LOGGING = { 'level': 'DEBUG', 'propagate': False }, + 'business_rules': { + 'handlers': ['business_rules'], + 'level': 'DEBUG', + 'propagate': False + }, }, } diff --git a/progress/views.py b/progress/views.py index 5b8b2de..97e9b3e 100644 --- a/progress/views.py +++ b/progress/views.py @@ -93,6 +93,7 @@ class CourseProgressUserView(APIView): class TeacherUpdateProgress(APIView): + permission_classes = (permissions.IsAuthenticated, ) renderer_classes = (JSONRenderer,) @staticmethod From e9c7cdf90ec22e7c819a8407482c55414de5fbaf Mon Sep 17 00:00:00 2001 From: Andrey Date: Sat, 14 Apr 2018 15:15:58 +0300 Subject: [PATCH 09/25] finance logging --- finance/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finance/models.py b/finance/models.py index 47ac532..fbe0ecf 100755 --- a/finance/models.py +++ b/finance/models.py @@ -28,7 +28,7 @@ class Bill(models.Model): return '%s: %s' % (self.id, self.user) def freeze_course(self, user): - if self.invoice_set.exclude('F').exists(): + if self.invoice_set.exclude(status='F').exists(): try: p = Progress.objects.get(user=user, course_token=self.course_token) p.freeze = True From 031fec5f8df71dfdf58492e0e9b695499cf7bbb4 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sat, 14 Apr 2018 15:34:38 +0300 Subject: [PATCH 10/25] finance logging --- finance/models.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/finance/models.py b/finance/models.py index fbe0ecf..76c55c4 100755 --- a/finance/models.py +++ b/finance/models.py @@ -34,14 +34,17 @@ class Bill(models.Model): p.freeze = True p.save() except Progress.DoesNotExist: - pass + logger_business_rules.info('Отказ от платежей прошёл успешно', exc_info=True, extra={ + 'description': 'The privileges were not taken away, as they were not granted', + 'user': user, + }) self.freeze = True self.save() else: logger_business_rules.warning('Попытка нарушения правила отказа от плотежей', exc_info=True, extra={ - 'description': 'Пользоваль пытается отказатся от платежей по счёту, однако все платежи уже оплачены', + 'description': 'All payments already paid', 'user': user, }) From a252a7c31608b64af7e25838006382f49933fb50 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sat, 14 Apr 2018 15:49:07 +0300 Subject: [PATCH 11/25] finance logging --- finance/models.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/finance/models.py b/finance/models.py index 76c55c4..f511f9c 100755 --- a/finance/models.py +++ b/finance/models.py @@ -29,11 +29,15 @@ class Bill(models.Model): def freeze_course(self, user): if self.invoice_set.exclude(status='F').exists(): + log = False try: p = Progress.objects.get(user=user, course_token=self.course_token) p.freeze = True p.save() except Progress.DoesNotExist: + log = True + + if log: logger_business_rules.info('Отказ от платежей прошёл успешно', exc_info=True, extra={ 'description': 'The privileges were not taken away, as they were not granted', 'user': user, From 07401183a90149483e8c350b96cccd2ae9f7678c Mon Sep 17 00:00:00 2001 From: Andrey Date: Sat, 14 Apr 2018 16:58:20 +0300 Subject: [PATCH 12/25] finance logging --- finance/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finance/models.py b/finance/models.py index f511f9c..89ad0ec 100755 --- a/finance/models.py +++ b/finance/models.py @@ -31,7 +31,7 @@ class Bill(models.Model): if self.invoice_set.exclude(status='F').exists(): log = False try: - p = Progress.objects.get(user=user, course_token=self.course_token) + p = Progress.objects.get(user=user, course_token=str(self.course_token)) p.freeze = True p.save() except Progress.DoesNotExist: From 861a4b25982f843902edbf89b992a7f0660092fa Mon Sep 17 00:00:00 2001 From: Andrey Date: Sat, 14 Apr 2018 17:03:10 +0300 Subject: [PATCH 13/25] finance logging --- finance/models.py | 2 +- finance/views.py | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/finance/models.py b/finance/models.py index 89ad0ec..baf8d70 100755 --- a/finance/models.py +++ b/finance/models.py @@ -31,7 +31,7 @@ class Bill(models.Model): if self.invoice_set.exclude(status='F').exists(): log = False try: - p = Progress.objects.get(user=user, course_token=str(self.course_token)) + p = Progress.objects.get(user=self.user, course_token=str(self.course_token)) p.freeze = True p.save() except Progress.DoesNotExist: diff --git a/finance/views.py b/finance/views.py index bb4e472..66a7df3 100644 --- a/finance/views.py +++ b/finance/views.py @@ -44,12 +44,13 @@ class FreezeView(APIView): @staticmethod def post(request, pk): - if request.user.is_authenticated: - try: - bill = Bill.objects.get(id=pk) - bill.freeze_course(request.user) - except Bill.DoesNotExist: - return Response("Счёт не найден", status=404) + try: + bill = Bill.objects.get(id=pk) + except Bill.DoesNotExist: + return Response("Счёт не найден", status=404) + + if request.user.is_authenticated and request.user.email == bill.user.email: + bill.freeze_course() return Response(status=204) return Response("Permission denied", status=403) From fb99799ec3b90700a4d2ab450592f573bd04cef1 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sat, 14 Apr 2018 17:06:04 +0300 Subject: [PATCH 14/25] finance logging --- finance/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finance/models.py b/finance/models.py index baf8d70..5d3c6cb 100755 --- a/finance/models.py +++ b/finance/models.py @@ -27,7 +27,7 @@ class Bill(models.Model): def __str__(self): return '%s: %s' % (self.id, self.user) - def freeze_course(self, user): + def freeze_course(self): if self.invoice_set.exclude(status='F').exists(): log = False try: From efa2f6037b31de5f680fdb1bf06d7aed1be60cd5 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sat, 14 Apr 2018 17:06:36 +0300 Subject: [PATCH 15/25] finance logging --- finance/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/finance/models.py b/finance/models.py index 5d3c6cb..f1c972f 100755 --- a/finance/models.py +++ b/finance/models.py @@ -40,7 +40,7 @@ class Bill(models.Model): if log: logger_business_rules.info('Отказ от платежей прошёл успешно', exc_info=True, extra={ 'description': 'The privileges were not taken away, as they were not granted', - 'user': user, + 'user': self.user, }) self.freeze = True @@ -49,7 +49,7 @@ class Bill(models.Model): else: logger_business_rules.warning('Попытка нарушения правила отказа от плотежей', exc_info=True, extra={ 'description': 'All payments already paid', - 'user': user, + 'user': self.user, }) def get_full_price(self): From ab1529ea955cf3112818f5d3006c072ea48b0435 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sat, 14 Apr 2018 17:10:27 +0300 Subject: [PATCH 16/25] finance logging --- finance/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finance/models.py b/finance/models.py index f1c972f..89a59f4 100755 --- a/finance/models.py +++ b/finance/models.py @@ -32,7 +32,7 @@ class Bill(models.Model): log = False try: p = Progress.objects.get(user=self.user, course_token=str(self.course_token)) - p.freeze = True + p.is_freeze = True p.save() except Progress.DoesNotExist: log = True From d1cbee1cae400cea47f33df0a48293079e4f04ce Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 16 Apr 2018 12:13:11 +0300 Subject: [PATCH 17/25] finance logging --- finance/models.py | 41 +++++++++++++++++++++++++++++++++++++++- finance/tasks.py | 48 +++++------------------------------------------ finance/views.py | 25 ++++++++++-------------- lms/celery.py | 14 ++++++++++++-- 4 files changed, 67 insertions(+), 61 deletions(-) diff --git a/finance/models.py b/finance/models.py index 89a59f4..f2d50b9 100755 --- a/finance/models.py +++ b/finance/models.py @@ -43,15 +43,54 @@ class Bill(models.Model): 'user': self.user, }) + msg = EmailMessage( + 'Вы откозались от оплаты по счёту', + """Вы откозались от оплаты по счёту. + Вы сможете продолжить оплату в личном кабинете""", + to=[self.user.email], + bcc=[self.opener.email], + reply_to=[self.opener.email], + ) + msg.send() + self.freeze = True self.save() else: - logger_business_rules.warning('Попытка нарушения правила отказа от плотежей', exc_info=True, extra={ + logger_business_rules.warning('Попытка нарушения правила отказа от платежей', exc_info=True, extra={ 'description': 'All payments already paid', 'user': self.user, }) + def unfreeze_course(self, force=False): + if force or self.invoice_set.exclude(status='F')\ + .filter(expected_date__lt=timezone.now() + relativedelta(days=1)).exists(): + if self.invoice_set.filter(status='F').exclude(expected_date__lt=timezone.now()).exists(): + try: + p = Progress.objects.get(user=self.user, course_token=str(self.course_token)) + p.is_freeze = False + p.save() + except Progress.DoesNotExist: + pass + + msg = EmailMessage( + 'Вы возобновили оплату по счёту', + """Вы возобновили оплату по счёту.""", + to=[self.user.email], + bcc=[self.opener.email], + reply_to=[self.opener.email], + ) + msg.send() + + self.freeze = False + self.save() + + else: + logger_business_rules.warning('Попытка нарушения правила возобновления платежей', exc_info=True, extra={ + 'description': 'Excepted date more than one day', + 'user': self.user, + }) + def get_full_price(self): return sum([i.price for i in self.invoice_set.all() if not i.price is None]) diff --git a/finance/tasks.py b/finance/tasks.py index b7912e4..d3d7244 100644 --- a/finance/tasks.py +++ b/finance/tasks.py @@ -1,62 +1,24 @@ -import json -from datetime import datetime, timedelta - import logging import os import requests -from django_celery_beat.models import CrontabSchedule, PeriodicTask from yandex_money.models import Payment from finance.models import InvoiceRebilling from lms import celery_app from django.conf import settings - logger_yandex = logging.getLogger('yandex_money') -def setup_periodic_billing(order_number): - # TODO: настроить периодичность и срок окончания - # 12:00 первого числа каждого месяца - logger_yandex.info("Оформдение новой рассрочки") - - schedule, _ = CrontabSchedule.objects.get_or_create( - minute='0', - hour='*/3', - ) - PeriodicTask.objects.create( - crontab=schedule, - name='Periodic billing (order_number={})'.format(order_number), - task='finance.tasks.periodic_billing', - kwargs=json.dumps({ - 'order_number': order_number - }), - expires=datetime.utcnow() + timedelta(days=180) # в течение полугода - ) - - @celery_app.task -def periodic_billing(order_number): - try: - sample = InvoiceRebilling.objects.get(yandex_pay__order_number=order_number) - except InvoiceRebilling.DoesNotExist: - raise ValueError('Номер заказа {} не найден'.format(order_number)) - - bill = sample.bill +def periodic_billing(): + logger_yandex.info("start periodic billing task") - invoice = InvoiceRebilling.objects.create( - status='P', - price=sample.price, - method=sample.method, - is_open=sample.is_open, - bill=bill, - ) + for invoice in InvoiceRebilling.objects.filter(method='Y').exclude(status='F'): - if invoice.method == 'Y': - user = bill.user + user = invoice.bill.user yandex_pay = Payment.objects.create( - invoice_id=sample.yandex_pay.invoice_id, order_amount=invoice.price, customer_number=user.id, user=user, @@ -72,7 +34,7 @@ def repeat_card_payment(invoice): resp = requests.post(settings.YANDEX_MONEY_MWS_URL + 'repeatCardPayment', data={ 'clientOrderId': invoice.id, # уникальное возрастающее целое число - 'invoiceId': invoice.yandex_pay.invoice_id, + 'invoiceId': invoice.key, 'amount': invoice.price, 'orderNumber': invoice.yandex_pay.order_number }, diff --git a/finance/views.py b/finance/views.py index 66a7df3..c866150 100644 --- a/finance/views.py +++ b/finance/views.py @@ -21,7 +21,6 @@ from courses.models import Course from courses.api import CourseParamsApi from finance.models import Bill, Invoice, InvoiceRebilling from finance.serializers import BillSerializer, InvoiceSerializer -from finance.tasks import setup_periodic_billing from lms.global_decorators import transaction_decorator from lms.tools import get_real_name from django.utils import timezone @@ -60,13 +59,13 @@ class UnFreezeView(APIView): @staticmethod def post(request, pk): - if request.user.is_authenticated: - try: - bill = Bill.objects.get(id=pk) - bill.freeze = False - bill.save() - except Bill.DoesNotExist: - return Response("Счёт не найден", status=404) + try: + bill = Bill.objects.get(id=pk) + except Bill.DoesNotExist: + return Response("Счёт не найден", status=404) + + if request.user.is_authenticated and request.user.email == bill.user.email: + bill.unfreeze_course() return Response(status=204) return Response("Permission denied", status=403) @@ -444,13 +443,10 @@ class YandexAvisoView(APIView): @staticmethod def post(request): data = dict() - rebilling = None for i in request.body.decode('utf-8').split('&'): key = i.split('=')[0] val = i.split('=')[1] data[key] = val - if key == 'rebillingOn': - rebilling = val try: pay = Payment.objects.get(order_number=data['orderNumber']) @@ -465,6 +461,9 @@ class YandexAvisoView(APIView): }) pay.shop_amount = data['shopSumAmount'] + invoice = pay.invoice + invoice.key = data['invoiceId'] + invoice.save() pay.status = Payment.STATUS.SUCCESS now = timezone.now() pay.performed_datetime = now.isoformat() @@ -495,10 +494,6 @@ class YandexAvisoView(APIView): msg.attach_alternative(html_content, "text/html") msg.send() - if rebilling: - InvoiceRebilling.objects.get(yandex_pay=pay) - setup_periodic_billing(pay.order_number) - return HttpResponse(xml_res, content_type='application/xml') diff --git a/lms/celery.py b/lms/celery.py index 5f3c106..fb1f536 100644 --- a/lms/celery.py +++ b/lms/celery.py @@ -3,6 +3,8 @@ import os from celery import Celery from raven import Client from raven.contrib.celery import register_signal, register_logger_signal +from celery.schedules import crontab + # set the default Django settings module for the 'celery' program. os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'lms.settings') @@ -13,11 +15,19 @@ app.config_from_object('django.conf:settings', namespace='CELERY') app.autodiscover_tasks() -client = Client('http://caaea487274f4e23a9107862484c79f3:3d463ad4717942508536f7a659921950@sentry.skillbox.ru/3') +client = Client('http://caaea487274f4e23a9107862484c79f3:3d463ad4717942508536f7a659921950@sentry.skillbox.ru/7') register_logger_signal(client) register_signal(client) @app.task(bind=True) def debug_task(self): - print('Request: {0!r}'.format(self.request)) \ No newline at end of file + print('Request: {0!r}'.format(self.request)) + + +CELERY_BEAT_SCHEDULE = { + 'periodic_billing': { + 'task': 'finance.tasks.periodic_billing', + 'schedule': crontab(minute='0',hour='*/3',), + }, +} \ No newline at end of file From 30343178cc5854cd47f4ad5b8069865876476a82 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 16 Apr 2018 12:19:09 +0300 Subject: [PATCH 18/25] finance logging --- lms/celery.py | 9 --------- lms/settings.py | 10 ++++++++++ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lms/celery.py b/lms/celery.py index fb1f536..78168d7 100644 --- a/lms/celery.py +++ b/lms/celery.py @@ -3,7 +3,6 @@ import os from celery import Celery from raven import Client from raven.contrib.celery import register_signal, register_logger_signal -from celery.schedules import crontab # set the default Django settings module for the 'celery' program. @@ -23,11 +22,3 @@ register_signal(client) @app.task(bind=True) def debug_task(self): print('Request: {0!r}'.format(self.request)) - - -CELERY_BEAT_SCHEDULE = { - 'periodic_billing': { - 'task': 'finance.tasks.periodic_billing', - 'schedule': crontab(minute='0',hour='*/3',), - }, -} \ No newline at end of file diff --git a/lms/settings.py b/lms/settings.py index ce5acd6..062bffd 100644 --- a/lms/settings.py +++ b/lms/settings.py @@ -4,6 +4,8 @@ import os import raven import environ import socket +from celery.schedules import crontab + root = environ.Path(__file__) - 2 env = environ.Env() @@ -31,6 +33,14 @@ CELERY_EMAIL_TASK_CONFIG = { 'ignore_result': False, } + +CELERY_BEAT_SCHEDULE = { + 'periodic_billing': { + 'task': 'finance.tasks.periodic_billing', + 'schedule': crontab(minute='0',hour='*/3',), + }, +} + CELERYD_TASK_TIME_LIMIT = 300 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) From ca0cc861fd7c7b5061c2e769d4ad117691d0166b Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 16 Apr 2018 12:35:22 +0300 Subject: [PATCH 19/25] finance logging --- lms/settings.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lms/settings.py b/lms/settings.py index 062bffd..1a9a793 100644 --- a/lms/settings.py +++ b/lms/settings.py @@ -33,8 +33,7 @@ CELERY_EMAIL_TASK_CONFIG = { 'ignore_result': False, } - -CELERY_BEAT_SCHEDULE = { +CELERYBEAT_SCHEDULE= { 'periodic_billing': { 'task': 'finance.tasks.periodic_billing', 'schedule': crontab(minute='0',hour='*/3',), From ece11c0dd6a0127ccca30ed499ead5fc911ae566 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 16 Apr 2018 12:55:55 +0300 Subject: [PATCH 20/25] finance logging --- lms/settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lms/settings.py b/lms/settings.py index 1a9a793..b0af80c 100644 --- a/lms/settings.py +++ b/lms/settings.py @@ -36,7 +36,8 @@ CELERY_EMAIL_TASK_CONFIG = { CELERYBEAT_SCHEDULE= { 'periodic_billing': { 'task': 'finance.tasks.periodic_billing', - 'schedule': crontab(minute='0',hour='*/3',), + 'schedule': crontab(minute='*/5',hour='*/3',), + # 'schedule': crontab(minute='0',hour='*/3',), }, } From a05f4e4151b323119118fee6fb077fcb2cbf4955 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 16 Apr 2018 13:09:40 +0300 Subject: [PATCH 21/25] finance logging --- finance/tasks.py | 26 +++++++++++++------------- lms/settings.py | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/finance/tasks.py b/finance/tasks.py index d3d7244..a034701 100644 --- a/finance/tasks.py +++ b/finance/tasks.py @@ -15,19 +15,19 @@ logger_yandex = logging.getLogger('yandex_money') def periodic_billing(): logger_yandex.info("start periodic billing task") - for invoice in InvoiceRebilling.objects.filter(method='Y').exclude(status='F'): - - user = invoice.bill.user - yandex_pay = Payment.objects.create( - order_amount=invoice.price, - customer_number=user.id, - user=user, - cps_email=user.email - ) - invoice.yandex_pay = yandex_pay - invoice.save() - - repeat_card_payment(invoice) + # for invoice in InvoiceRebilling.objects.filter(method='Y').exclude(status='F'): + # + # user = invoice.bill.user + # yandex_pay = Payment.objects.create( + # order_amount=invoice.price, + # customer_number=user.id, + # user=user, + # cps_email=user.email + # ) + # invoice.yandex_pay = yandex_pay + # invoice.save() + # + # repeat_card_payment(invoice) def repeat_card_payment(invoice): diff --git a/lms/settings.py b/lms/settings.py index b0af80c..a700424 100644 --- a/lms/settings.py +++ b/lms/settings.py @@ -36,7 +36,7 @@ CELERY_EMAIL_TASK_CONFIG = { CELERYBEAT_SCHEDULE= { 'periodic_billing': { 'task': 'finance.tasks.periodic_billing', - 'schedule': crontab(minute='*/5',hour='*/3',), + 'schedule': crontab(minute='*/1'), # 'schedule': crontab(minute='0',hour='*/3',), }, } From 6f0c6500a553ce5d1e4977b0c20ed5a9b7317398 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 16 Apr 2018 13:19:29 +0300 Subject: [PATCH 22/25] finance logging --- lms/celery.py | 8 ++++++++ lms/settings.py | 9 --------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/lms/celery.py b/lms/celery.py index 78168d7..9ba6937 100644 --- a/lms/celery.py +++ b/lms/celery.py @@ -3,6 +3,7 @@ import os from celery import Celery from raven import Client from raven.contrib.celery import register_signal, register_logger_signal +from celery.schedules import crontab # set the default Django settings module for the 'celery' program. @@ -18,6 +19,13 @@ client = Client('http://caaea487274f4e23a9107862484c79f3:3d463ad4717942508536f7a register_logger_signal(client) register_signal(client) +@app.on_after_configure.connect +def setup_periodic_tasks(sender, **kwargs): + sender.add_periodic_task( + # crontab(minute='0',hour='*/3',), + crontab(minute='*/1'), + 'finance.tasks.periodic_billing' + ) @app.task(bind=True) def debug_task(self): diff --git a/lms/settings.py b/lms/settings.py index a700424..adb44a1 100644 --- a/lms/settings.py +++ b/lms/settings.py @@ -4,7 +4,6 @@ import os import raven import environ import socket -from celery.schedules import crontab root = environ.Path(__file__) - 2 @@ -33,14 +32,6 @@ CELERY_EMAIL_TASK_CONFIG = { 'ignore_result': False, } -CELERYBEAT_SCHEDULE= { - 'periodic_billing': { - 'task': 'finance.tasks.periodic_billing', - 'schedule': crontab(minute='*/1'), - # 'schedule': crontab(minute='0',hour='*/3',), - }, -} - CELERYD_TASK_TIME_LIMIT = 300 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) From e9bd627f57e57a7d57df4183733d297d993428cf Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 16 Apr 2018 13:22:25 +0300 Subject: [PATCH 23/25] finance logging --- lms/celery.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lms/celery.py b/lms/celery.py index 9ba6937..5d9ca81 100644 --- a/lms/celery.py +++ b/lms/celery.py @@ -4,6 +4,7 @@ from celery import Celery from raven import Client from raven.contrib.celery import register_signal, register_logger_signal from celery.schedules import crontab +from finance.tasks import periodic_billing # set the default Django settings module for the 'celery' program. @@ -19,14 +20,16 @@ client = Client('http://caaea487274f4e23a9107862484c79f3:3d463ad4717942508536f7a register_logger_signal(client) register_signal(client) + @app.on_after_configure.connect def setup_periodic_tasks(sender, **kwargs): sender.add_periodic_task( # crontab(minute='0',hour='*/3',), crontab(minute='*/1'), - 'finance.tasks.periodic_billing' + periodic_billing(), ) + @app.task(bind=True) def debug_task(self): print('Request: {0!r}'.format(self.request)) From 7031294f132158985e251e1ea7ea27ec818309c1 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 16 Apr 2018 13:26:18 +0300 Subject: [PATCH 24/25] finance logging --- lms/celery.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lms/celery.py b/lms/celery.py index 5d9ca81..7654575 100644 --- a/lms/celery.py +++ b/lms/celery.py @@ -4,7 +4,6 @@ from celery import Celery from raven import Client from raven.contrib.celery import register_signal, register_logger_signal from celery.schedules import crontab -from finance.tasks import periodic_billing # set the default Django settings module for the 'celery' program. @@ -20,14 +19,10 @@ client = Client('http://caaea487274f4e23a9107862484c79f3:3d463ad4717942508536f7a register_logger_signal(client) register_signal(client) - -@app.on_after_configure.connect -def setup_periodic_tasks(sender, **kwargs): - sender.add_periodic_task( - # crontab(minute='0',hour='*/3',), - crontab(minute='*/1'), - periodic_billing(), - ) +app.conf.beat_schedule = { + crontab(minute='*/1'), + 'finance.tasks.periodic_billing' +} @app.task(bind=True) From 3a7b3825f752687ccc7fab7983856e9bcdd6785b Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 16 Apr 2018 13:28:37 +0300 Subject: [PATCH 25/25] finance logging --- lms/celery.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lms/celery.py b/lms/celery.py index 7654575..95c4803 100644 --- a/lms/celery.py +++ b/lms/celery.py @@ -20,8 +20,11 @@ register_logger_signal(client) register_signal(client) app.conf.beat_schedule = { - crontab(minute='*/1'), - 'finance.tasks.periodic_billing' + 'periodic_billing': { + 'schedule': crontab(minute='*/1'), + # crontab(minute='0',hour='*/3',), + 'task': 'finance.tasks.periodic_billing' + } }