From d7e89aff1d15b26fb26c2f002e796ef1bdfbe292 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 20 Mar 2018 15:15:05 +0300 Subject: [PATCH 1/2] finance logging --- finance/models.py | 6 ++ finance/serializers.py | 2 +- finance/views.py | 146 +++++++++++++++++++++++++---------------- 3 files changed, 97 insertions(+), 57 deletions(-) diff --git a/finance/models.py b/finance/models.py index ae56954..a881d33 100755 --- a/finance/models.py +++ b/finance/models.py @@ -20,6 +20,12 @@ class Bill(models.Model): def get_full_price(self): return sum([i.price for i in self.invoice_set.all() if not i.price is None]) + def check_validate(self): + return self.invoice_set.filter(is_open=True).count() == 1 + + def check_pay(self): + return self.invoice_set.filter(status="F").exists() + class Meta: verbose_name = 'Счет' verbose_name_plural = 'Счета' diff --git a/finance/serializers.py b/finance/serializers.py index a166c1c..6930f5a 100644 --- a/finance/serializers.py +++ b/finance/serializers.py @@ -32,7 +32,7 @@ class InvoiceSerializer(serializers.ModelSerializer): class Meta: model = Invoice - exclude = ('bill',) + fields = '__all__' @staticmethod def get_status(self): diff --git a/finance/views.py b/finance/views.py index 4a4de84..507c5fa 100644 --- a/finance/views.py +++ b/finance/views.py @@ -41,7 +41,6 @@ class BillListView(APIView): if request.user.is_authenticated and (request.user.groups.filter(name__in=['managers','lead_managers']).exists() or request.user.is_superuser): bill = request.JSON.get('bill') - children = request.JSON.get('children', []) if bill: user = get_user_model().objects.get(email=bill['user']) @@ -60,66 +59,102 @@ class BillListView(APIView): bill_obj.comment = comment bill_obj.save() - for i in children: - status = get_real_name(elem=i['status'], array=Invoice.BILL_STATUSES) - try: - invoice_id = i['id'] - except KeyError: - invoice_id = None - - try: - if not invoice_id is None: - invoice = Invoice.objects.get(id=i['id']) - if invoice.status == "P" or invoice.status == status: - continue - else: - raise Invoice.DoesNotExist - - except Invoice.DoesNotExist: - i['method'] = get_real_name(elem=i['method'], array=Invoice.BILL_METHOD) - i['status'] = status - i['bill'] = bill_obj - i['yandex_pay'] = None - invoice = Invoice.objects.create(**i) - - if i['method'] == 'Y' and invoice.yandex_pay is None: - yandex_pay = Payment.objects.create( - order_amount=i['price'], - shop_amount=0, - customer_number=bill_obj.user.id, - user=bill_obj.user, - cps_email=bill_obj.user.email, - ) - invoice.yandex_pay = yandex_pay - invoice.save() - - msg = EmailMessage( - 'Выставлен новый счёт.', - '''Менеджер %s выставил счёт пользователю %s на курс "%s".''' - % ( - invoice.bill.opener.get_full_name(), - invoice.bill.user.email, - Course.objects.get(token=invoice.bill.course_token).title, - ), - 'robo@skillbox.ru', - [invoice.bill.opener.email], - bcc=['dmitry.dolya@skillbox.ru'], - ) - - msg.send() - - res = { - "bill": BillSerializer(bill_obj).data, - "children": [InvoiceSerializer(i).data for i in bill_obj.invoice_set.all()], - } - - return Response(res, status=200) + return Response(BillSerializer(bill_obj).data, status=200) return Response("Bill not set", status=400) return Response("Course detail access only for manager users", status=403) +class InvoiceDetailView(APIView): + renderer_classes = (JSONRenderer,) + + @staticmethod + def delete(request): + invoice_id = request.JSON.get('invoice_id', None) + if invoice_id is None: + return Response("invoice_id must be set", status=400) + + try: + i = Invoice.objects.get(id=invoice_id) + if not i.status == "F" and not (i.bill.check_pay() and i.is_open): + i.delete() + except Invoice.DoesNotExist: + pass + + return Response(status=204) + + @transaction_decorator + def post(self, request): + if request.user.is_authenticated and (request.user.groups.filter(name__in=['managers','lead_managers']).exists() + or request.user.is_superuser): + invoice_data = request.JSON.get('invoice', None) + if invoice_data is None: + return Response("Invoice mast be set", status=400) + + try: + bill = Bill.objects.get(id=invoice_data.pop('bill')) + except (Bill.DoesNotExist, KeyError): + return Response('Bill id must be set', status=400) + + if bill.check_validate() and invoice_data['is_open']: + return Response("Уже есть платёж открывающий курс", status=400) + + try: + invoice = Invoice.objects.get(id=invoice_data['id']) + except (Invoice.DoesNotExist, KeyError): + if bill.check_pay(): + return Response( + "Нельзя добавить новый платёж, так как один из платежей по счёту уже оплачен", status=400) + invoice = Invoice.objects.create(**invoice_data) + + if invoice.status == "F": + return Response(InvoiceSerializer(invoice).data, status=200) + + invoice.real_price = None + invoice.method = invoice_data['method'] + invoice.status = invoice_data['status'] + if invoice.status == "F": + invoice.real_price = invoice_data['real_price'] + + if bill.check_pay() and (invoice.price < invoice_data['price']): + return Response("""Нельзя менять стоимость по счёту в большую сторону, + когда один из платежей оплачен""", status=400) + + invoice.price = invoice_data['price'] + invoice.is_open = invoice_data['is_open'] + + if invoice.method == 'Y' and invoice.yandex_pay is None: + yandex_pay = Payment.objects.create( + order_amount=invoice.price, + shop_amount=0, + customer_number=bill.user.id, + user=bill.user, + cps_email=bill.user.email, + ) + invoice.yandex_pay = yandex_pay + + msg = EmailMessage( + 'Выставлен новый счёт.', + '''Менеджер %s выставил счёт пользователю %s на курс "%s".''' + % ( + invoice.bill.opener.get_full_name(), + invoice.bill.user.email, + Course.objects.get(token=invoice.bill.course_token).title, + ), + 'robo@skillbox.ru', + [invoice.bill.opener.email], + bcc=['dmitry.dolya@skillbox.ru'], + ) + + msg.send() + invoice.save() + + return Response(InvoiceSerializer(invoice).data, status=200) + + return Response("Invoice detail access only for manager users", status=403) + + class BillDetailView(APIView): renderer_classes = (JSONRenderer,) status_code = 200 @@ -343,7 +378,6 @@ class YandexAvisoView(APIView): 'response': xml_res, }) - msg = EmailMessage( 'Успешная оплата.', '''Пользователь "%s", перевёл %s рублей. Номер платежа в яндекс кассе %s''' From 7506946cd43c1aae3f64c07b3f6672c73fd47da1 Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 22 Mar 2018 12:04:54 +0300 Subject: [PATCH 2/2] finance logging --- access/views.py | 5 ++- courses/models.py | 8 ++-- courses/urls.py | 2 + courses/views.py | 96 ++++++++++++++++++++++++++++++++++------------- finance/views.py | 3 ++ 5 files changed, 82 insertions(+), 32 deletions(-) diff --git a/access/views.py b/access/views.py index 5915943..f698ae0 100644 --- a/access/views.py +++ b/access/views.py @@ -28,7 +28,10 @@ class TeacherListView(APIView): status_code = 200 def get(self, request): - return Response([i.email for i in get_user_model().objects.filter(groups__name='teachers')], self.status_code) + return Response([{ + 'email': i.email, + 'token': i.out_key, + } for i in get_user_model().objects.filter(groups__name='teachers')], self.status_code) class ResetPasswordView(APIView): diff --git a/courses/models.py b/courses/models.py index 3bc61ac..ce50bf7 100755 --- a/courses/models.py +++ b/courses/models.py @@ -58,8 +58,6 @@ class Lesson(models.Model): class Topic(models.Model): course = models.ForeignKey(to="Course", verbose_name='курс') title = models.CharField(verbose_name='Название', max_length=255) - description = models.TextField(verbose_name='Описание', blank=True, null=True) - icon = models.ImageField(verbose_name='Иконка темы', null=True, blank=True) sort = models.SmallIntegerField(verbose_name='Поле сортировки') def __str__(self): @@ -75,12 +73,12 @@ class Topic(models.Model): class CourseManager(models.Manager): def update_or_create_course(self, image=None, big_image=None, statistic=None, - big_mobile_image=None, slug=None, teachers=None, + big_mobile_image=None, slug=None, teacher_tokens=None, level=None, direction=None, **kwargs): slug = slug if slug else slugify(unidecode.unidecode(kwargs['title'])) - kwargs['teacher_tokens'] = teachers + kwargs['teacher_tokens'] = teacher_tokens if image: path = 'course/image%s.png' % slug @@ -101,7 +99,7 @@ class CourseManager(models.Manager): kwargs['level'] = get_real_name(COURSE_LEVEL, level) if direction: - kwargs['direction'] = get_real_name(COURSE_DIRECTION, direction) + kwargs['direction'] = get_real_name(COURSE_DIRECTION, direction[0]) try: course = self.get(slug=slug) diff --git a/courses/urls.py b/courses/urls.py index 819f814..3c78dbf 100644 --- a/courses/urls.py +++ b/courses/urls.py @@ -7,5 +7,7 @@ urlpatterns = [ url(r'lesson/(?P.+)/$', views.LessonDetail.as_view()), url(r'tree/(?P.+)/$', views.TreeView.as_view()), url(r'detail/(?P.+)/$', views.CourseDetailView.as_view()), + url(r'topic/update/$', views.UpdateTopicView.as_view()), + url(r'topic/delete/(?P[0-9]{1,99})/$', views.DeleteTopicView.as_view()), url(r'^$', views.CourseListView.as_view()), ] \ No newline at end of file diff --git a/courses/views.py b/courses/views.py index 5db4617..0b7f74e 100644 --- a/courses/views.py +++ b/courses/views.py @@ -1,17 +1,19 @@ +from django.db.models import F from jwt import DecodeError -from courses.models import Course, Lesson +from courses.models import Course, Lesson, Topic from rest_framework.renderers import JSONRenderer from rest_framework.response import Response from rest_framework.views import APIView from django.contrib.auth import get_user_model -from courses.serializers import CourseDetailSerializer, CourseTreeSerializer, LessonSerializer, TeacherLessonSerializer +from courses.serializers import CourseDetailSerializer, CourseTreeSerializer, LessonSerializer, TeacherLessonSerializer, \ + TopicSerializer import jwt from courses.tasks import add_lesson from lms import settings - +import json class TreeView(APIView): renderer_classes = (JSONRenderer,) @@ -31,27 +33,6 @@ class CourseListView(APIView): status_code = 200 def post(self, request): - """ - This API endpoint create/update course. - --- - parameters: - - name: level - type: string - required: true - location: form - - name: direction - type: string - required: true - location: form - - name: statistic - type: string - required: true - location: form - ... - """ - # TODO: Костыль - teachers_emails = request.JSON.get('teachers', []) - request.JSON['teachers'] = [get_user_model().objects.get(email=i).out_key for i in teachers_emails] course = Course.objects.update_or_create_course(**request.JSON.dict()) return Response(CourseDetailSerializer(course).data, status=self.status_code) @@ -80,6 +61,69 @@ class CourseDetailView(APIView): return Response(CourseDetailSerializer(Course.objects.get(slug=slug)).data, self.status_code) +class DeleteTopicView(APIView): + renderer_classes = (JSONRenderer,) + + @staticmethod + def delete(request, topic_id): + try: + t = Topic.objects.get(id=topic_id) + except Topic.DoesNotExist: + return Response("Темы не существует", status=404) + t.delete() + return Response(CourseTreeSerializer(t.course).data, status=200) + + +class UpdateTopicView(APIView): + renderer_classes = (JSONRenderer,) + + @staticmethod + def post(request): + topic_id = request.JSON.get('id', None) + sort = request.JSON.get('sort', None) + course_token = request.JSON.get('course_token', None) + title = request.JSON.get('title', None) + + if course_token is None: + return Response("Не передан course_token", status=400) + + if sort is None: + return Response("Не передан sort", status=400) + + try: + course = Course.objects.get(token=course_token) + except Course.DoesNotExist: + return Response("Курс не найден", status=404) + + try: + if topic_id: + t = Topic.objects.get(id=topic_id) + if not t.sort == sort: + for topic in reversed(): + topic.sort = topic.sort + 1 + topic.save() + t.sort = sort + t.title = t.title if title is None else title + t.save() + else: + raise Topic.DoesNotExist() + + except Topic.DoesNotExist: + if title is None: + return Response("Не передан title", status=400) + + for topic in reversed(course.topic_set.filter(sort__gte=sort)): + topic.sort = topic.sort + 1 + topic.save() + + Topic.objects.create( + course=course, + title=title, + sort=sort, + ) + return Response(CourseTreeSerializer(course).data, status=200) + + class LessonInfoView(APIView): renderer_classes = (JSONRenderer,) status_code = 200 @@ -109,7 +153,7 @@ class LessonDetail(APIView): l = LessonSerializer(lesson).data try: - payload = None if jwt_token is None\ + payload = None if jwt_token is None \ else jwt.decode(jwt_token, settings.COURSE_PROGRESS_SECRET_KEY, algorithms=['HS256']) except DecodeError: payload = None @@ -144,7 +188,7 @@ class LessonDetail(APIView): if not new_lesson: return Response("Permission denied", status=403) - #TODO Задача для селери + # TODO Задача для селери add_lesson(request.user.out_key, course.token, lesson.token, course.get_teacher(), lesson.is_hm) return Response(l, status=200) diff --git a/finance/views.py b/finance/views.py index 507c5fa..494d022 100644 --- a/finance/views.py +++ b/finance/views.py @@ -97,6 +97,9 @@ class InvoiceDetailView(APIView): except (Bill.DoesNotExist, KeyError): return Response('Bill id must be set', status=400) + invoice_data['method'] = get_real_name(elem=invoice_data['method'], array=Invoice.BILL_METHOD) + invoice_data['status'] = get_real_name(elem=invoice_data['status'], array=Invoice.BILL_METHOD) + if bill.check_validate() and invoice_data['is_open']: return Response("Уже есть платёж открывающий курс", status=400)