From cfe1238b670b67e5bd3fb9eaa11383eeca4646c6 Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 9 Nov 2017 14:59:35 +0300 Subject: [PATCH] =?UTF-8?q?=D0=AF=D0=BD=D0=B4=D0=B5=D0=BA=D1=81=20=D0=B4?= =?UTF-8?q?=D0=B5=D0=BD=D1=8C=D0=B3=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- access/models.py | 2 - access/tasks.py | 40 ------ csv/load_bills.py | 27 ++-- finance/migrations/0009_auto_20171108_1930.py | 20 +++ finance/migrations/0010_auto_20171109_1140.py | 22 +++ finance/models.py | 5 +- finance/pay.py | 91 ------------- finance/tasks.py | 67 ++++----- finance/views.py | 21 ++- finance/yandex_pay.py | 127 ------------------ journals/tasks.py | 79 ----------- lms/settings.py | 18 ++- lms/urls.py | 2 +- 13 files changed, 123 insertions(+), 398 deletions(-) delete mode 100755 access/tasks.py create mode 100644 finance/migrations/0009_auto_20171108_1930.py create mode 100644 finance/migrations/0010_auto_20171109_1140.py delete mode 100755 finance/pay.py mode change 100755 => 100644 finance/tasks.py delete mode 100644 finance/yandex_pay.py delete mode 100755 journals/tasks.py diff --git a/access/models.py b/access/models.py index 0c1e67d..46aff9e 100755 --- a/access/models.py +++ b/access/models.py @@ -1,9 +1,7 @@ # encoding=utf-8 import random import string -import json -from celery.result import AsyncResult from django.contrib.contenttypes.models import ContentType from django_celery_results.models import TaskResult diff --git a/access/tasks.py b/access/tasks.py deleted file mode 100755 index 93bc02e..0000000 --- a/access/tasks.py +++ /dev/null @@ -1,40 +0,0 @@ -# coding=utf-8 -from celery.task import periodic_task -from datetime import timedelta, datetime -from access.models import User, TrafSource, TrafTokenHistory - - -# @periodic_task(run_every=timedelta(minutes=20)) -# def check_prepo_delay(): -# # Проверка просроченых задач преподавателя -# for res in User.objects.filter(delay_date__lte=datetime.now(), in_role='T'): -# res.delay = False -# res.delay_description = '' -# res.delay_date = None -# res.save() -# -# -# @periodic_task(run_every=timedelta(minutes=1)) -# def check_online(): -# for user in User.objects.filter(status='ON', last_time__lte=datetime.now()-timedelta(minutes=10)): -# user.status = 'OFF' -# user.save() -# -# -# @periodic_task(run_every=timedelta(hours=1)) -# def check_traf_source(): -# for source in TrafSource.objects.filter(on=True).exclude(live_time=None): -# if source.token and source.token_start + timedelta(hours=source.live_time) < datetime.now(): -# source.on = False -# TrafTokenHistory.objects.create(token=source.token, source=source, live_time=source.live_time, date_start=source.token_start, date_end=datetime.now()) -# source.token = None -# source.token_start = None -# source.save() - - -#@periodic_task(run_every=timedelta(minutes=1)) -#def sync_users(): -# for user in User.objects.filter(sync=False): -# result = sent_sync_user(user) -# if result: -# user._set_synced() diff --git a/csv/load_bills.py b/csv/load_bills.py index d1da97c..fa8a4ca 100644 --- a/csv/load_bills.py +++ b/csv/load_bills.py @@ -1,6 +1,7 @@ import os, sys, django, csv from django.contrib.auth import get_user_model +from django.db import IntegrityError sys.path.append("../") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings") @@ -20,7 +21,7 @@ if __name__ == '__main__': bill_kwarg['course'] = Course.objects.get(id=row.pop('course__id', None)) opener_id = row.pop('opener__id', None) bill_kwarg['opener'] = get_user_model().objects.get(id=opener_id) if opener_id \ - else get_user_model().objects.get(email="kate.gazukina@skillbox.ru") + else get_user_model().objects.get(email="kate.gazukina@skillbox.ru") email = row.pop('user__email', None) try: @@ -35,14 +36,20 @@ if __name__ == '__main__': bill_kwarg['comment'] = row.pop('comment', None) bill_kwarg['description'] = row.pop('description', None) - bill = Bill.objects.create(**bill_kwarg) try: - price = int(row.pop('price', None)) - except ValueError: - price = None + bill = Bill.objects.create(**bill_kwarg) + method = row.pop('bill_method', None) + try: + price = int(row.pop('price', None)) + except ValueError: + price = None - try: - real_price = int(row.pop('real_price', None)) - except ValueError: - real_price = None - Invoice.objects.create(bill=bill, price=price, real_price=real_price, **row) \ No newline at end of file + try: + real_price = int(row.pop('real_price', None)) + except ValueError: + real_price = None + + Invoice.objects.create(bill=bill, method=method, price=price, real_price=real_price, **row) + + except IntegrityError: + pass diff --git a/finance/migrations/0009_auto_20171108_1930.py b/finance/migrations/0009_auto_20171108_1930.py new file mode 100644 index 0000000..f6eb227 --- /dev/null +++ b/finance/migrations/0009_auto_20171108_1930.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-11-08 19:30 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('finance', '0008_auto_20171108_1435'), + ] + + operations = [ + migrations.AlterField( + model_name='invoice', + name='key', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Ключ платежа'), + ), + ] diff --git a/finance/migrations/0010_auto_20171109_1140.py b/finance/migrations/0010_auto_20171109_1140.py new file mode 100644 index 0000000..b6c8c55 --- /dev/null +++ b/finance/migrations/0010_auto_20171109_1140.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-11-09 11:40 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('courses', '0007_auto_20171103_1627'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('finance', '0009_auto_20171108_1930'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='bill', + unique_together=set([('course', 'opener', 'user')]), + ), + ] diff --git a/finance/models.py b/finance/models.py index 82a0588..22de0bd 100755 --- a/finance/models.py +++ b/finance/models.py @@ -21,6 +21,9 @@ class Bill(models.Model): class Meta: verbose_name = 'Счет' verbose_name_plural = 'Счета' + unique_together = ( + ('course', 'opener', 'user',), + ) class Invoice(models.Model): @@ -43,7 +46,7 @@ class Invoice(models.Model): real_price = models.IntegerField(verbose_name='Полученная сумма', null=True, blank=True, help_text='Сумма, минус комиссия') method = models.CharField(verbose_name='Способ оплаты', max_length=2, default='Y', choices=BILL_METHOD) - key = models.CharField(verbose_name='Ключ платежа', blank=True, max_length=255, default='') + key = models.CharField(verbose_name='Ключ платежа', blank=True, max_length=255, null=True) comment = models.TextField(verbose_name='Комментарий продавца', help_text='Будет показано пользователю', blank=True, editable=False) bill = models.ForeignKey(to=Bill, verbose_name="Связный счёт") diff --git a/finance/pay.py b/finance/pay.py deleted file mode 100755 index 4a71f45..0000000 --- a/finance/pay.py +++ /dev/null @@ -1,91 +0,0 @@ -# coding=utf-8 -## Реализация платежных сервисов -from django.shortcuts import render -from lms.decors import response_decor -from finance.models import Bill -from django.http import Http404 -from lms.tools import gen_pay_sig - - -@response_decor(template='good_pay.html', without_auth=True) -def success(request): - # Страница успешного платежа - if request.GET.get('sp_sig') and gen_pay_sig(request.GET) == request.GET.get('sp_sig'): - try: - bill = Bill.objects.get(id=request.GET['sp_order_id']) - - except Bill.DoesNotExist: - raise Http404 - - else: - bill.status = 'F' if request.GET['sp_result'] == '1' else 'C' - bill.out_id = request.GET['sp_payment_id'] - bill._method = 'S' - bill.save() - - url = '/?success_pay=True' if bill.service.freepay else '/?success_pay=True&course_id={0}'.format(bill.service.course.id) - if bill.admitad_uid: - url += '&admitad_uid={0}&order_id={1}&email={2}'.format(bill.admitad_uid, bill.id, bill.user.email) - - return {'redirect': url} - else: - raise Http404 - - -@response_decor(template='fail_pay.html', without_auth=True) -def fail(request): - # Страница ошибки платежа - if request.GET.get('sp_sig') and gen_pay_sig(request.GET) == request.GET.get('sp_sig'): - try: - bill = Bill.objects.get(id=request.GET['sp_order_id']) - - except Bill.DoesNotExist: - raise Http404 - - else: - bill.status = 'F' if request.GET['sp_result'] else 'C' - bill.out_id = request.GET['sp_payment_id'] - bill._method = 'S' - bill.save() - - url = '/?fail_pay=True&fail_url={0}'.format(bill.service.url) if bill.service.freepay else '/?fail_pay=True&fail_pay_id={0}'.format(bill.id) - return {'redirect': url} - else: - raise Http404 - - -def result(request): - # Страница получения результата о платежах - if request.GET.get('sp_sig') and gen_pay_sig(request.GET) == request.GET.get('sp_sig'): - try: - bill = Bill.objects.get(id=request.GET['sp_order_id']) - except Bill.DoesNotExist: - keys = {'sp_salt': request.GET['sp_salt'], - 'sp_status': 'error', - 'sp_description': 'Order not found'} - - return render(request, 'simplepayresult.xml', - {"salt": request.GET['sp_salt'], - 'status': 'error', - 'description': 'Order not found', - 'sig': gen_pay_sig(keys)}, - content_type="application/xhtml+xml") - - else: - bill.status = 'F' if request.GET['sp_result'] == '1' else 'C' - bill.out_id = request.GET['sp_payment_id'] - bill._method = 'S' - bill.save() - - keys = {'sp_salt': request.GET['sp_salt'], - 'sp_status': 'ok', - 'sp_description': 'Good pay'} - - return render(request, 'simplepayresult.xml', - {'salt': request.GET['sp_salt'], - 'status': 'ok', - 'sig': gen_pay_sig(keys), - 'description': 'Good pay'}, - content_type="application/xhtml+xml") - else: - raise Http404 diff --git a/finance/tasks.py b/finance/tasks.py old mode 100755 new mode 100644 index 2fe4319..10009ed --- a/finance/tasks.py +++ b/finance/tasks.py @@ -1,40 +1,27 @@ -# coding=utf-8 -from celery.task import periodic_task -from datetime import timedelta, datetime - -from django.db.models import Q - -from finance.models import Price, Bill, ServiceRequest -from courses.models import CourseMap - - -# @periodic_task(run_every=timedelta(minutes=1)) -# def price_map_migrator(): -# for price in Price.objects.filter(included=None).exclude(course=None): -# if not price.included.exists(): -# inc_type = [] -# -# if price.m_type == 'B': -# inc_type = ['B'] -# elif price.m_type == 'E': -# inc_type = ['B', 'E'] -# elif price.m_type == 'P': -# inc_type = ['B', 'E', 'P'] -# for m in CourseMap.objects.filter(course=price.course): -# if m.get_obj().theme.price_type in inc_type: -# price.included.add(m) -# -# -# @periodic_task(run_every=timedelta(hours=1)) -# def price_map_migrator(): -# for price in Price.objects.exclude(by_time=None): -# for bill in Bill.objects.filter(service=price, fire_date__lt=datetime.now()): -# bill.status = 'H' -# bill.save() -# -# -# @periodic_task(run_every=timedelta(minutes=1)) -# def sent_service_request_to_amo(): -# sr = ServiceRequest.objects.filter(send=False, send_date__lt=datetime.now()).exclude(Q(name='TEST')| Q(status='E')).first() -# if sr: -# sr.sent_to_amo() +from __future__ import absolute_import, unicode_literals +from celery import shared_task +import requests +from django.core.mail import send_mail + + +@shared_task +def send_to_yandex(pay): + r = requests.post('https://money.yandex.ru/eshop.xml', data={ + 'shopId': pay.shop_id, + 'scid': pay.scid, + 'sum': pay.order_amount, + 'customerNumber': pay.customer_number, + 'orderNumber': pay.order_number, + 'cps_email': pay.cps_email, + }) + body = { + "subject": 'Спасибо за регистрацию', + "message": '''Вам выставлен счёт, для оплаты перейдите по ссылке %s''' % r.url, + "from_email": 'robo@skillbox.ru', + "recipient_list": [pay.cps_email], + } + + send_mail( + **body + ) + return r.url diff --git a/finance/views.py b/finance/views.py index 9c5abf5..6ca9931 100644 --- a/finance/views.py +++ b/finance/views.py @@ -1,12 +1,14 @@ from django.contrib.auth import get_user_model from courses.models import Course +from finance.tasks import send_to_yandex from lms.global_decorators import transaction_decorator from rest_framework.views import APIView from rest_framework.renderers import JSONRenderer from rest_framework.response import Response from django.db.models import Q +from yandex_money.models import Payment from finance.models import Bill, Invoice from finance.serializers import BillSerializer, InvoiceSerializer from lms.tools import get_real_name @@ -34,18 +36,25 @@ class BillListView(APIView): if bill: bill['user'] = get_user_model().objects.get(email=bill['user']) bill['opener'] = get_user_model().objects.get(email=bill['opener']) - bill['course'] = Course.objects.get(title=bill['course']) + bill['course'] = Course.objects.get(title=bill['course'][0]) bill_obj, is_create = Bill.objects.update_or_create(**bill) invoices = bill_obj.invoice_set.all() for i in children: - i['method'] = get_real_name(elem=i['method'], array=Invoice.BILL_METHOD) - i['status'] = get_real_name(elem=i['status'], array=Invoice.BILL_STATUSES) + i['method'] = get_real_name(elem=i['method'][0], array=Invoice.BILL_METHOD) + i['status'] = get_real_name(elem=i['status'][0], array=Invoice.BILL_STATUSES) i['bill'] = bill_obj invoice, _is_create = Invoice.objects.update_or_create(**i) invoices = [j for j in invoices if not j.id == invoice.id] if invoice.method == 'Y' and invoice.status == 'P': - print("Отправить письмо") + pay, _is_create = Payment.objects.get_or_create( + order_amount=invoice.price, + order_number=bill_obj.id, + customer_number=bill_obj.user.id, + user=bill_obj.user, + cps_email=bill_obj.user.email, + ) + send_to_yandex(pay) [i.delete() for i in invoices] @@ -81,4 +90,6 @@ class BillDetailView(APIView): res, status=self.status_code, ) - return Response("Permission denied", status=403) \ No newline at end of file + return Response("Permission denied", status=403) + + diff --git a/finance/yandex_pay.py b/finance/yandex_pay.py deleted file mode 100644 index 6707370..0000000 --- a/finance/yandex_pay.py +++ /dev/null @@ -1,127 +0,0 @@ -# coding=utf-8 -import datetime -from hashlib import md5 - -from django.shortcuts import render -from django.views.decorators.csrf import csrf_exempt - -from finance.models import Bill, YandexKassaHistory -from settings.settings import YANDEX_MONEY_SHOP_PASSWORD - - -def gen_md5(cd): - return md5(';'.join(( - cd['action'], - cd['orderSumAmount'], - cd['orderSumCurrencyPaycash'], - cd['orderSumBankPaycash'], - cd['shopId'], - cd['invoiceId'], - cd['customerNumber'], - YANDEX_MONEY_SHOP_PASSWORD, - )).encode('utf-8')).hexdigest().upper() - - -@csrf_exempt -def aviso(request): - # Страница получения результата о платежах - data = request.POST - tmp = {} - for key, value in data.items(): - tmp[key] = value - tmp = str(tmp) - _time = datetime.datetime.now() - _log = YandexKassaHistory.objects.create(text=tmp, _type='paymentAvisoResponse') - result = { - 'performedDatetime': _time.isoformat(), - 'code': '200', - 'shopId': data.get('shopId'), - 'invoiceId': data.get('invoiceId'), - 'message': '' - } - if not data.get('md5'): - return render(request, 'yandexkassa_result.xml', {'check_type': 'paymentAvisoResponse', 'data': result}, - content_type="application/xhtml+xml") - - if data['md5'] == gen_md5(data): - try: - bill = Bill.objects.get(id=data['orderNumber']) - except Bill.DoesNotExist: - result['message'] = u'Не найден счет по указанным данным' - - else: - if str(bill.user.id) == data['customerNumber']: - bill.status = 'F' - bill.real_price = data['shopSumAmount'] - bill.out_id = data['invoiceId'] - bill.status_changed = _time - bill.save() - result['code'] = '0' - - else: - result['message'] = u'Не найден пользователь, указанный в запросе' - - else: - result['code'] = '1' - result['message'] = u'Не сходится ключ платежа' - - return render(request, 'yandexkassa_result.xml', {'check_type': 'paymentAvisoResponse', 'data': result}, - content_type="application/xhtml+xml") - - -@csrf_exempt -def check(request): - # Страница получения результата о платежах - data = request.POST - _time = datetime.datetime.now() - tmp = {} - for key, value in data.items(): - tmp[key] = value - tmp = str(tmp) - if data.get('action'): - YandexKassaHistory.objects.create(text=tmp, _type=data['action']) - else: - YandexKassaHistory.objects.create(text=tmp, _type='empty_check') - - result = { - 'performedDatetime': _time.isoformat(), - 'code': '200' if data.get('action') == 'checkOrder' else '1', - 'shopId': data.get('shopId'), - 'orderSumAmount': data.get('orderSumAmount'), - 'invoiceId': data.get('invoiceId'), - 'message': '' - } - if not data.get('md5'): - return render(request, 'yandexkassa_result.xml', - {'check_type': 'checkOrderResponse', - 'data': result}, - content_type="application/xhtml+xml") - - if data['md5'] == gen_md5(data): - try: - bill = Bill.objects.get(id=data['orderNumber']) - except Bill.DoesNotExist: - result['message'] = u'Не найден счет по указанным данным' - - else: - - if str(bill.user.id) == data['customerNumber']: - if data['action'] != 'checkOrder': - bill.status = 'B' - bill.out_id = data['invoiceId'] - bill.status_changed = _time - bill._method = 'Y' - bill.inside_data = tmp - bill.test = True - bill.save() - result['code'] = '0' - - else: - result['message'] = u'Не найден пользователь, указанный в запросе' - else: - result['code'] = '1' - result['message'] = u'Не сходится ключ платежа' - result['tech_message'] = str(data['md5']) + str('_') + str(gen_md5(data)) - - return render(request, 'yandexkassa_result.xml', {'check_type': 'checkOrderResponse' if data['action'] == 'checkOrder' else 'cancelOrderResponse', 'data': result}, - content_type="application/xhtml+xml") diff --git a/journals/tasks.py b/journals/tasks.py deleted file mode 100755 index 5c530ed..0000000 --- a/journals/tasks.py +++ /dev/null @@ -1,79 +0,0 @@ -# coding=utf-8 -from datetime import timedelta, datetime - -from celery.task import periodic_task -from django.core.files import File -from management.letters import sent_new_expired - -from access.models import User -from journals.models import ExamTry, HomeworkTry, DiplomaJ -from settings.settings import TEACHER - - -# @periodic_task(run_every=timedelta(hours=1)) -# def check_expired(): -# # Проверка просроченых задач преподавателя -# for res in (ExamTry.objects.filter(f_date=None).exclude(expired=True), -# HomeworkTry.objects.filter(f_date=None).exclude(expired=True)): -# for _try in res: -# if _try.parent.get_status_flag() not in ['N', 'F']: -# # Проверка экзамена -# if _try.date + timedelta(days=1) < datetime.now(): -# _try.expired = True -# _try.save() -# for user in User.objects.filter(in_role='S2'): -# sent_new_expired(_try, user.email) -# -# -# @periodic_task(run_every=timedelta(minutes=1)) -# def check_robo_prep(): -# teacher = User.objects.get(email=TEACHER) -# handler = [] -# for journal in HomeworkTry.objects.filter(teacher=teacher, f_date=None).exclude(success=True, date=None).order_by( -# 'date'): -# if journal.parent.get_status_flag not in ['N', 'F']: -# handler.append(journal) -# -# for journal in ExamTry.objects.filter(teacher=teacher, f_date=None).exclude(success=True, date=None).order_by( -# 'date'): -# if journal.parent.get_status_flag not in ['N', 'F']: -# handler.append(journal) -# -# print(handler) -# for _tr in handler: -# for _cm in _tr.comments.all(): -# s = False -# print(_cm.text) -# if 'GO_TO_SUCCESS' in _cm.text: -# _tr.success = True -# _tr.f_date = datetime.now() -# s = True -# elif 'GO_TO_FAIL' in _cm.text: -# _tr.f_date = datetime.now() -# s = True -# if s: -# _tr.save() -# -# -# #@periodic_task(run_every=timedelta(minutes=3)) -# #def block_warning(): -# # for tm in CourseThemeJ.objects.filter(material__empty=True): -# # for n in CourseThemeJ.objects.filter(material__sort__gte=tm.material.sort-1, f_date=None).exclude(date=None): -# # for u in User.objects.filter(in_role='A'): -# # letters.block_warning(n, n.material.course.get_title(), u) -# -# -# @periodic_task(run_every=timedelta(minutes=3)) -# def gen_in_diploma(): -# for tm in DiplomaJ.objects.filter(gen=False): -# if not tm.out_image or not tm.in_image: -# _in, _out = tm.draw_key(*tm.draw_name(*tm.draw_date())) -# if _in: -# _in = open(_in, 'rb') -# tm.in_image.save(File(_in).name, File(_in), save=True) -# -# if _out: -# _out = open(_out, 'rb') -# tm.out_image.save(File(_out).name, File(_out), save=True) -# tm.gen = True -# tm.save() diff --git a/lms/settings.py b/lms/settings.py index 5533051..76ff9b6 100644 --- a/lms/settings.py +++ b/lms/settings.py @@ -3,6 +3,13 @@ import os import raven import environ +import socket + +try: + HOSTNAME = socket.gethostname() +except: + HOSTNAME = 'localhost:8000' + root = environ.Path(__file__) - 2 env = environ.Env() @@ -54,9 +61,15 @@ AMO_USER_LOGIN = 'baryshnikov@mokselle.com' AMO_USER_HASH = 'd639c26c1c1900e5a8cee66cd3395bdc' AMO_SUBDOMAIN = 'mokselle' DEFAULT_FROM_EMAIL = 'robo@skillbox.ru' + +YANDEX_MONEY_DEBUG = False +YANDEX_MONEY_SCID = '149639' +YANDEX_MONEY_SHOP_ID = '157133' YANDEX_MONEY_SHOP_PASSWORD = 'nu5Xefise' -YANDEX_SHOP_ID = '157133' -YANDEX_scid = '149639' +YANDEX_MONEY_FAIL_URL = 'https://%s/finance/fail/' % HOSTNAME +YANDEX_MONEY_SUCCESS_URL = 'https://%s/finance/access/' % HOSTNAME +# информировать о случаях, когда модуль вернул Яндекс.Кассе ошибку +YANDEX_MONEY_MAIL_ADMINS_ON_PAYMENT_ERROR = True # Application definition # место куда сохраняем пользовательские файлы @@ -87,6 +100,7 @@ INSTALLED_APPS = [ 'django_celery_results', 'django_celery_beat', "djcelery_email", + 'yandex_money', 'raven.contrib.django.raven_compat', 'access', 'courses', diff --git a/lms/urls.py b/lms/urls.py index a23e50e..8fc300a 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -9,5 +9,5 @@ urlpatterns = [ url(r'^admin/', include(admin.site.urls)), url(r'^media/(?P.*)/$', serve, {'document_root': settings.MEDIA_ROOT}), url(r'^static/(?P.*)/$', serve, {'document_root': settings.STATIC_ROOT}), - + url(r'^yandex-money/', include('yandex_money.urls')), ]