From f967b4de473c764f4f0f9e6892b5e1b4debcfe8c Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Wed, 28 Feb 2018 14:03:21 +0300 Subject: [PATCH 01/41] LIL-263. Add cause field to AuthorBalance model --- .../migrations/0011_authorbalance_cause.py | 18 ++++++++++++++++++ apps/payment/models.py | 1 + 2 files changed, 19 insertions(+) create mode 100644 apps/payment/migrations/0011_authorbalance_cause.py diff --git a/apps/payment/migrations/0011_authorbalance_cause.py b/apps/payment/migrations/0011_authorbalance_cause.py new file mode 100644 index 00000000..21b5c2c2 --- /dev/null +++ b/apps/payment/migrations/0011_authorbalance_cause.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.2 on 2018-02-28 11:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('payment', '0010_auto_20180227_0933'), + ] + + operations = [ + migrations.AddField( + model_name='authorbalance', + name='cause', + field=models.TextField(default='', verbose_name='Причина отказа'), + ), + ] diff --git a/apps/payment/models.py b/apps/payment/models.py index 4cdacf68..e25fb6ab 100644 --- a/apps/payment/models.py +++ b/apps/payment/models.py @@ -34,6 +34,7 @@ class AuthorBalance(models.Model): commission = models.DecimalField('Комиссия', max_digits=8, decimal_places=2, default=0) status = models.PositiveSmallIntegerField('Статус', choices=STATUS_CHOICES, default=0) payment = models.OneToOneField('Payment', on_delete=models.CASCADE, null=True, blank=True, verbose_name='Платёж') + cause = models.TextField('Причина отказа', default='') class Meta: verbose_name = 'Баланс' From 9abc6602025b904181bcd029bb3fcaf015f54452 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Wed, 28 Feb 2018 14:05:30 +0300 Subject: [PATCH 02/41] LIL-263. Add cause field to AuthorBalanceSerializer --- api/v1/serializers/payment.py | 1 + 1 file changed, 1 insertion(+) diff --git a/api/v1/serializers/payment.py b/api/v1/serializers/payment.py index dd5029dc..93400cbf 100644 --- a/api/v1/serializers/payment.py +++ b/api/v1/serializers/payment.py @@ -15,6 +15,7 @@ class AuthorBalanceSerializer(serializers.ModelSerializer): 'commission', 'status', 'payment', + 'cause', ) read_only_fields = ( From 81163bdff54344f47dc4ee78ae7bbbffa395d222 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Wed, 28 Feb 2018 17:24:56 +0300 Subject: [PATCH 03/41] Add day_discount field to SchoolScheduleSerializer --- api/v1/serializers/school.py | 1 + 1 file changed, 1 insertion(+) diff --git a/api/v1/serializers/school.py b/api/v1/serializers/school.py index 29e76ad1..fa3b0a4f 100644 --- a/api/v1/serializers/school.py +++ b/api/v1/serializers/school.py @@ -15,6 +15,7 @@ class SchoolScheduleSerializer(serializers.ModelSerializer): 'materials', 'age', 'month_price', + 'day_discount', ) read_only_fields = ( From 2a8dc4d0ea7c34141df8aa51c02b723e261efc7b Mon Sep 17 00:00:00 2001 From: Sanasol Date: Wed, 28 Feb 2018 22:14:30 +0700 Subject: [PATCH 04/41] [LIL-258][LIL-262][LIL-265] --- project/templates/lilcity/index.html | 6 ++--- web/src/js/modules/popup.js | 34 ++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/project/templates/lilcity/index.html b/project/templates/lilcity/index.html index 2f8c70e9..6ec65f05 100644 --- a/project/templates/lilcity/index.html +++ b/project/templates/lilcity/index.html @@ -440,7 +440,7 @@
{% for school_schedule in school_schedules %}
Итого, за месяц:
-
1800р.
+
1800р.
@@ -471,7 +471,7 @@
{% comment %}В ссылке, в параметре запроса weekdays, нужно указать выбранные дни недели{% endcomment %} - ПЕРЕЙТИ К ОПЛАТЕ + ПЕРЕЙТИ К ОПЛАТЕ
diff --git a/web/src/js/modules/popup.js b/web/src/js/modules/popup.js index ba2e04d1..583b8863 100644 --- a/web/src/js/modules/popup.js +++ b/web/src/js/modules/popup.js @@ -47,4 +47,38 @@ $(document).ready(function () { popup.removeClass('open'); }, 300); } + + var selectedWeekdays = {}; + $(document).on('change', '[data-day]', function(){ + var weekday = $(this).data('day'); + var price = $(this).data('price'); + var discount = $(this).data('discount'); + if($(this).is(':checked')) { + console.log('checked'); + selectedWeekdays[weekday] = {price:price, discount:discount}; + } else { + console.log('not checked'); + delete selectedWeekdays[weekday]; + } + + var weekdays = [], price = 0, discount = 0; + for(var i in selectedWeekdays) { + price += parseInt(selectedWeekdays[i].price); + discount += parseInt(selectedWeekdays[i].discount); + weekdays.push(i); + } + + var text = ''; + if(weekdays.length >= 7) { + text = ''+price+' '+(price-discount)+'р.'; + } else { + text = price+'p.'; + } + $('.order_price_text').html(text); + + var link = $('.but_btn_popup').data('link'); + + link = link+'?'+decodeURIComponent($.param({weekdays: weekdays})); + $('.but_btn_popup').attr('href', link); + }); }); \ No newline at end of file From ce2d76b505f8873bc3430929b3a445f391193846 Mon Sep 17 00:00:00 2001 From: Sanasol Date: Wed, 28 Feb 2018 22:30:17 +0700 Subject: [PATCH 05/41] popup link --- web/src/js/modules/popup.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/js/modules/popup.js b/web/src/js/modules/popup.js index 583b8863..75b1a877 100644 --- a/web/src/js/modules/popup.js +++ b/web/src/js/modules/popup.js @@ -78,7 +78,7 @@ $(document).ready(function () { var link = $('.but_btn_popup').data('link'); - link = link+'?'+decodeURIComponent($.param({weekdays: weekdays})); + link = link+'?'+decodeURIComponent($.param({weekdays: weekdays}, true)); $('.but_btn_popup').attr('href', link); }); }); \ No newline at end of file From 44f9e2859cf08a1a7cea209305ebf02aa1aa9e3b Mon Sep 17 00:00:00 2001 From: Sanasol Date: Wed, 28 Feb 2018 22:39:20 +0700 Subject: [PATCH 06/41] selected days display --- web/src/js/modules/popup.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/src/js/modules/popup.js b/web/src/js/modules/popup.js index 75b1a877..c6c5f86a 100644 --- a/web/src/js/modules/popup.js +++ b/web/src/js/modules/popup.js @@ -50,6 +50,7 @@ $(document).ready(function () { var selectedWeekdays = {}; $(document).on('change', '[data-day]', function(){ + var days = ['', 'Понедельник', 'Вторник', 'Среда', 'Четврг', 'Пятница', 'Суббота', 'Воскресенье']; var weekday = $(this).data('day'); var price = $(this).data('price'); var discount = $(this).data('discount'); @@ -61,11 +62,12 @@ $(document).ready(function () { delete selectedWeekdays[weekday]; } - var weekdays = [], price = 0, discount = 0; + var weekdays = [], daysText = [], price = 0, discount = 0; for(var i in selectedWeekdays) { price += parseInt(selectedWeekdays[i].price); discount += parseInt(selectedWeekdays[i].discount); weekdays.push(i); + daysText.push(days[i]); } var text = ''; @@ -75,6 +77,7 @@ $(document).ready(function () { text = price+'p.'; } $('.order_price_text').html(text); + $('.order__days').html(daysText.join(', ')); var link = $('.but_btn_popup').data('link'); From f0749a6b2590e932b725115009e3b06210e0a699 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Fri, 2 Mar 2018 10:18:05 +0300 Subject: [PATCH 07/41] Filter AuthorBalance by author id --- api/v1/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/v1/views.py b/api/v1/views.py index 6958934c..bca328a2 100644 --- a/api/v1/views.py +++ b/api/v1/views.py @@ -45,7 +45,7 @@ class AuthorBalanceViewSet(ExtendedModelViewSet): queryset = AuthorBalance.objects.all() serializer_class = AuthorBalanceSerializer permission_classes = (IsAdmin,) - filter_fields = ('status', 'type') + filter_fields = ('author', 'status', 'type') search_fields = ( 'author__email', 'author__first_name', From 79bc43f8cd3db5315dbac9608f5247d7e8d03305 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Fri, 2 Mar 2018 10:19:32 +0300 Subject: [PATCH 08/41] Set nullable cause field of AuthorBalance --- apps/payment/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/payment/models.py b/apps/payment/models.py index e25fb6ab..dc8426dd 100644 --- a/apps/payment/models.py +++ b/apps/payment/models.py @@ -34,7 +34,7 @@ class AuthorBalance(models.Model): commission = models.DecimalField('Комиссия', max_digits=8, decimal_places=2, default=0) status = models.PositiveSmallIntegerField('Статус', choices=STATUS_CHOICES, default=0) payment = models.OneToOneField('Payment', on_delete=models.CASCADE, null=True, blank=True, verbose_name='Платёж') - cause = models.TextField('Причина отказа', default='') + cause = models.TextField('Причина отказа', null=True, blank=True) class Meta: verbose_name = 'Баланс' From 8603a92e101b3042e0a4535319b44dc2b06856f1 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Fri, 2 Mar 2018 10:24:08 +0300 Subject: [PATCH 09/41] Fix AuthorBalanceSerializer --- api/v1/serializers/payment.py | 28 ++++++++++++++++++++++++++++ api/v1/views.py | 7 +++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/api/v1/serializers/payment.py b/api/v1/serializers/payment.py index 93400cbf..9174177d 100644 --- a/api/v1/serializers/payment.py +++ b/api/v1/serializers/payment.py @@ -1,9 +1,37 @@ from rest_framework import serializers from apps.payment.models import AuthorBalance +from .user import UserSerializer + + +class AuthorBalanceCreateSerializer(serializers.ModelSerializer): + + class Meta: + model = AuthorBalance + fields = ( + 'id', + 'author', + 'type', + 'amount', + 'commission', + 'status', + 'payment', + 'cause', + ) + + read_only_fields = ( + 'id', + 'author', + 'type', + 'payment', + ) + + def to_representation(self, instance): + return AuthorBalanceSerializer(instance, context=self.context).to_representation(instance) class AuthorBalanceSerializer(serializers.ModelSerializer): + author = UserSerializer() class Meta: model = AuthorBalance diff --git a/api/v1/views.py b/api/v1/views.py index bca328a2..ad4b1402 100644 --- a/api/v1/views.py +++ b/api/v1/views.py @@ -24,7 +24,7 @@ from .serializers.content import ( ImageObjectSerializer, ) from .serializers.school import SchoolScheduleSerializer -from .serializers.payment import AuthorBalanceSerializer +from .serializers.payment import AuthorBalanceSerializer, AuthorBalanceCreateSerializer from .serializers.user import ( UserSerializer, UserPhotoSerializer, ) @@ -43,7 +43,10 @@ User = get_user_model() class AuthorBalanceViewSet(ExtendedModelViewSet): queryset = AuthorBalance.objects.all() - serializer_class = AuthorBalanceSerializer + serializer_class = AuthorBalanceCreateSerializer + serializer_class_map = { + 'list': AuthorBalanceSerializer, + } permission_classes = (IsAdmin,) filter_fields = ('author', 'status', 'type') search_fields = ( From 87291cb4e013e76492adf7e2652aec7ee7d955e8 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Fri, 2 Mar 2018 10:41:03 +0300 Subject: [PATCH 10/41] Add AuthorBalance migration --- .../migrations/0012_auto_20180302_0740.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 apps/payment/migrations/0012_auto_20180302_0740.py diff --git a/apps/payment/migrations/0012_auto_20180302_0740.py b/apps/payment/migrations/0012_auto_20180302_0740.py new file mode 100644 index 00000000..bc5a3414 --- /dev/null +++ b/apps/payment/migrations/0012_auto_20180302_0740.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.2 on 2018-03-02 07:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('payment', '0011_authorbalance_cause'), + ] + + operations = [ + migrations.AlterField( + model_name='authorbalance', + name='cause', + field=models.TextField(blank=True, null=True, verbose_name='Причина отказа'), + ), + ] From 0f0be5a92a1f85cb9b4decfb89c4c1ff80623d65 Mon Sep 17 00:00:00 2001 From: Sanasol Date: Fri, 2 Mar 2018 15:27:17 +0700 Subject: [PATCH 11/41] [LIL-281] --- web/src/sass/_common.sass | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/web/src/sass/_common.sass b/web/src/sass/_common.sass index 0739fe00..f553d957 100755 --- a/web/src/sass/_common.sass +++ b/web/src/sass/_common.sass @@ -3339,10 +3339,14 @@ a flex-wrap: wrap &__block flex: 0 0 calc(50% - 20px) + +m + flex: 0 0 calc(100% - 20px) &__list display: flex margin: 0 -10px flex-wrap: wrap + +m + margin-top: 20px &__item display: block margin: 0 10px 40px 10px @@ -3366,10 +3370,14 @@ a margin-top: 20px a width: 70% + +m + width: 100%; &__btn__prev margin-top: 12px a width: 70% + +m + width: 100%; &__month margin-bottom: 20px flex: 0 0 320px @@ -3377,6 +3385,8 @@ a background-image: linear-gradient(-225deg, #FFE2EB 0%, #D8F5F5 100%) width: 70% padding: 2px + +m + width: 100%; &__wrap padding: 5px 0 10px background: white From a65375f0a8451a3d8a3043d153c8ba7d387aa19d Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Fri, 2 Mar 2018 13:00:30 +0300 Subject: [PATCH 12/41] Add SchoolSchedules fixture & fix other fixtures --- apps/course/fixtures/course.json | 730 ++++++++++----------- apps/school/fixtures/school_schedules.json | 93 +++ apps/user/fixtures/superuser.json | 66 +- 3 files changed, 491 insertions(+), 398 deletions(-) create mode 100644 apps/school/fixtures/school_schedules.json diff --git a/apps/course/fixtures/course.json b/apps/course/fixtures/course.json index 6b818598..9f243591 100644 --- a/apps/course/fixtures/course.json +++ b/apps/course/fixtures/course.json @@ -1,367 +1,367 @@ [ - { - "model": "course.course", - "pk": 1, - "fields": { - "author": 1, - "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", - "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", - "from_author": "", - "price": "1500.00", - "is_infinite": false, - "deferred_start_at": null, - "category": 2, - "duration": 1, - "is_featured": false, - "status": 1, - "created_at": "2018-01-27T07:04:41.113Z", - "update_at": "2018-01-31T15:03:47.118Z", - "likes": [], - "materials": [] - } - }, - { - "model": "course.course", - "pk": 2, - "fields": { - "author": 1, - "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", - "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", - "from_author": "", - "price": "1900.00", - "is_infinite": false, - "deferred_start_at": null, - "category": 1, - "duration": 1, - "is_featured": false, - "status": 1, - "created_at": "2018-01-27T07:09:03.437Z", - "update_at": "2018-01-31T15:03:47.115Z", - "likes": [], - "materials": [] - } - }, - { - "model": "course.course", - "pk": 3, - "fields": { - "author": 1, - "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", - "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", - "from_author": "", - "price": "100.00", - "is_infinite": false, - "deferred_start_at": null, - "category": 9, - "duration": 1, - "is_featured": false, - "status": 1, - "created_at": "2018-01-27T07:09:03.442Z", - "update_at": "2018-01-31T15:03:47.112Z", - "likes": [], - "materials": [] - } - }, - { - "model": "course.course", - "pk": 4, - "fields": { - "author": 1, - "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", - "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", - "from_author": "", - "price": "400.00", - "is_infinite": false, - "deferred_start_at": null, - "category": 8, - "duration": 1, - "is_featured": false, - "status": 1, - "created_at": "2018-01-27T07:09:03.445Z", - "update_at": "2018-01-31T15:03:47.108Z", - "likes": [], - "materials": [] - } - }, - { - "model": "course.course", - "pk": 5, - "fields": { - "author": 1, - "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", - "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", - "from_author": "", - "price": "1800.00", - "is_infinite": false, - "deferred_start_at": null, - "category": 7, - "duration": 1, - "is_featured": false, - "status": 1, - "created_at": "2018-01-27T07:09:03.449Z", - "update_at": "2018-01-31T15:03:47.104Z", - "likes": [], - "materials": [] - } - }, - { - "model": "course.course", - "pk": 6, - "fields": { - "author": 1, - "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", - "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", - "from_author": "", - "price": "100.00", - "is_infinite": false, - "deferred_start_at": null, - "category": 6, - "duration": 1, - "is_featured": false, - "status": 1, - "created_at": "2018-01-27T07:09:03.452Z", - "update_at": "2018-01-31T15:03:47.101Z", - "likes": [], - "materials": [] - } - }, - { - "model": "course.course", - "pk": 7, - "fields": { - "author": 1, - "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", - "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", - "from_author": "", - "price": "1600.00", - "is_infinite": false, - "deferred_start_at": null, - "category": 5, - "duration": 1, - "is_featured": false, - "status": 1, - "created_at": "2018-01-27T07:09:03.455Z", - "update_at": "2018-01-31T15:03:47.097Z", - "likes": [], - "materials": [] - } - }, - { - "model": "course.course", - "pk": 8, - "fields": { - "author": 1, - "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", - "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", - "from_author": "", - "price": "1900.00", - "is_infinite": false, - "deferred_start_at": null, - "category": 4, - "duration": 1, - "is_featured": false, - "status": 1, - "created_at": "2018-01-27T07:09:03.458Z", - "update_at": "2018-01-31T15:03:47.093Z", - "likes": [], - "materials": [] - } - }, - { - "model": "course.course", - "pk": 9, - "fields": { - "author": 1, - "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", - "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", - "from_author": "", - "price": "200.00", - "is_infinite": false, - "deferred_start_at": null, - "category": 3, - "duration": 1, - "is_featured": false, - "status": 1, - "created_at": "2018-01-27T07:09:03.461Z", - "update_at": "2018-01-31T15:03:47.089Z", - "likes": [], - "materials": [] - } - }, - { - "model": "course.course", - "pk": 10, - "fields": { - "author": 1, - "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", - "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", - "from_author": "", - "price": "800.00", - "is_infinite": false, - "deferred_start_at": null, - "category": 2, - "duration": 1, - "is_featured": true, - "status": 1, - "created_at": "2018-01-27T07:09:03.464Z", - "update_at": "2018-01-31T15:03:47.086Z", - "likes": [], - "materials": [ - 1, - 2, - 3 - ] - } - }, - { - "model": "course.course", - "pk": 11, - "fields": { - "author": 1, - "title": "\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u0443\u0440\u0441 \u0434\u043b\u044f \u0434\u0435\u0442\u0435\u0439 \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438", - "short_description": "\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0442\u044f\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0438\u0437 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0444\u043e\u0440\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u0435\u043b\u044b\u0439 \u0438 \u0445\u0430\u0440\u0438\u0437\u043c\u0430\u0442\u0438\u0447\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439.", - "from_author": "", - "price": "100.00", - "is_infinite": false, - "deferred_start_at": "2018-02-28T12:00:00Z", - "category": 1, - "duration": 1, - "is_featured": false, - "status": 1, - "created_at": "2018-01-27T07:09:03.467Z", - "update_at": "2018-01-31T15:03:47.080Z", - "likes": [], - "materials": [ - 1, - 2, - 3 - ] - } - }, - { - "model": "course.category", - "pk": 1, - "fields": { - "title": "\u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436" - } - }, - { - "model": "course.category", - "pk": 2, - "fields": { - "title": "\u0430\u043a\u0432\u0430\u0440\u0435\u043b\u044c" - } - }, - { - "model": "course.category", - "pk": 3, - "fields": { - "title": "\u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u044f" - } - }, - { - "model": "course.category", - "pk": 4, - "fields": { - "title": "\u0430\u043d\u0438\u043c\u0430\u0446\u0438\u044f" - } - }, - { - "model": "course.category", - "pk": 5, - "fields": { - "title": "\u043f\u0430\u0441\u0442\u0435\u043b\u044c" - } - }, - { - "model": "course.category", - "pk": 6, - "fields": { - "title": "\u043f\u043b\u0430\u0441\u0442\u0438\u043b\u0438\u043d" - } - }, - { - "model": "course.category", - "pk": 7, - "fields": { - "title": "\u043a\u0440\u0435\u0430\u0442\u0438\u0432\u043d\u043e\u0435 \u043c\u044b\u0448\u043b\u0435\u043d\u0438\u0435" - } - }, - { - "model": "course.category", - "pk": 8, - "fields": { - "title": "\u043c\u043e\u0442\u043e\u0440\u0438\u043a\u0430" - } - }, - { - "model": "course.category", - "pk": 9, - "fields": { - "title": "\u0436\u0438\u0432\u043e\u043f\u0438\u0441\u044c" - } - }, - { - "model": "course.lesson", - "pk": 1, - "fields": { - "title": "1 \u0423\u0420\u041e\u041a", - "short_description": "\u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u0441\u044e\u0436\u0435\u0442, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430 \u043a\u0443\u0440\u0441\u0435 \u0438 \u0433\u043b\u0430\u0432\u043d\u043e\u0433\u043e \u0433\u0435\u0440\u043e\u044f \u0432\u0430\u0448\u0435\u0439 \u0438\u0441\u0442\u043e\u0440\u0438\u0438. \u0421 \u044d\u0442\u0438\u043c \u0433\u0435\u0440\u043e\u0435\u043c \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430 \u043f\u0440\u043e\u0442\u044f\u0436\u0435\u043d\u0438\u0438 \u0432\u0441\u0435\u0433\u043e \u043a\u0443\u0440\u0441\u0430.", - "course": 11, - "created_at": "2018-01-31T15:06:14.830Z", - "update_at": "2018-01-31T15:06:14.830Z" - } - }, - { - "model": "course.lesson", - "pk": 2, - "fields": { - "title": "2 \u0423\u0420\u041e\u041a", - "short_description": "\u0421\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0438 \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u0441 \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0433\u0435\u0440\u043e\u044f, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u043d\u044b\u0445 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0435\u0439 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0430. \u041f\u043e \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0443 \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u00ab\u0440\u0435\u0444\u0435\u0440\u0435\u043d\u0441\u044b\u00bb. \u0420\u0438\u0441\u0443\u0435\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u043e\u0431\u0440\u0430\u0437\u0430 \u0432 \u0441\u0432\u043e\u0435\u043c \u0441\u0442\u0438\u043b\u0435.\r\n\r\n\u0421\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u0438\u0437 \u043d\u0438\u0445 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0430. \u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u0441\u0430\u043c\u044b\u0435 \u0443\u0434\u0430\u0447\u043d\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b, \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u043c \u0438\u0445 \u0432 \u043e\u0434\u043d\u043e\u043c \u043d\u0430\u0431\u0440\u043e\u0441\u043a\u0435.", - "course": 11, - "created_at": "2018-01-31T15:06:46.772Z", - "update_at": "2018-01-31T15:06:46.772Z" - } - }, - { - "model": "course.lesson", - "pk": 3, - "fields": { - "title": "3 \u0423\u0420\u041e\u041a", - "short_description": "\u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u043e\u0436\u0438\u0432\u0438\u0442\u044c \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0430. \u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c 5 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u043d\u044b\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0434\u043b\u044f \u0432\u0430\u0448\u0435\u0433\u043e \u0433\u0435\u0440\u043e\u044f \u0438 \u0442\u043e\u0433\u043e \u0441\u044e\u0436\u0435\u0442\u0430, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043e\u043d \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0443\u0447\u0430\u0441\u0442\u0438\u0435, \u0440\u0438\u0441\u0443\u0435\u043c \u044d\u0441\u043a\u0438\u0437\u044b \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0430 \u0432 \u0440\u0430\u0437\u043d\u044b\u0445 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u043d\u044b\u0445 \u0434\u043b\u044f \u043d\u0435\u0433\u043e \u043f\u043e\u0437\u0430\u0445 \u0438 \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u0438.\r\n\r\n\u0412\u044b\u0434\u0435\u043b\u044f\u0435\u043c 5 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u043d\u044b\u0445 \u044d\u043c\u043e\u0446\u0438\u0439 \u0434\u043b\u044f \u0433\u0435\u0440\u043e\u044f, \u043d\u0430\u0434 \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c. \u041f\u043e \u044d\u043c\u043e\u0446\u0438\u044f\u043c \u043f\u043e\u0434\u0431\u0438\u0440\u0430\u0435\u043c \u0440\u0435\u0444\u0435\u0440\u0435\u043d\u0441\u044b \u0438 \u0441\u0442\u0438\u043b\u0438\u0437\u0443\u0435\u043c \u0438\u0445 \u0432 \u0441\u0432\u043e\u0435\u043c \u0441\u0442\u0438\u043b\u0435.", - "course": 11, - "created_at": "2018-01-31T15:07:08.979Z", - "update_at": "2018-01-31T15:07:08.979Z" - } - }, - { - "model": "course.material", - "pk": 1, - "fields": { - "title": "\u0411\u0443\u043c\u0430\u0433\u0430 \u0430\u043a\u0432\u0430\u0440\u0435\u043b\u044c\u043d\u0430\u044f", - "short_description": "\u0411\u0443\u043c\u0430\u0433\u0430 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0430\u043a\u0432\u0430\u0440\u0435\u043b\u044c\u044e \u0438\u043c\u0435\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435. \u042d\u0442\u043e \u043e\u0431\u044a\u044f\u0441\u043d\u044f\u0435\u0442\u0441\u044f \u0442\u0435\u043c, \u0447\u0442\u043e \u0430\u043a\u0432\u0430\u0440\u0435\u043b\u044c \u2014 \u043a\u0440\u0430\u0441\u043a\u0430 \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u0430\u044f, \u0430 \u0437\u043d\u0430\u0447\u0438\u0442 \u0444\u0430\u043a\u0442\u0443\u0440\u0430 \u0431\u0443\u043c\u0430\u0433\u0438 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0441\u0438\u043b\u044c\u043d\u043e \u0432\u043b\u0438\u044f\u0442\u044c \u043d\u0430 \u0432\u043d\u0435\u0448\u043d\u0438\u0439 \u0432\u0438\u0434 \u043a\u0440\u0430\u0441\u043e\u0447\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u044f.", - "created_at": "2018-01-31T14:55:48.394Z", - "update_at": "2018-01-31T14:55:48.394Z" - } - }, - { - "model": "course.material", - "pk": 2, - "fields": { - "title": "\u041a\u0438\u0441\u0442\u043e\u0447\u043a\u0438 \u0434\u043b\u044f \u0440\u0438\u0441\u043e\u0432\u0430\u043d\u0438\u044f", - "short_description": "\u041a\u0438\u0441\u0442\u044c \u2014 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u043f\u043e\u043a\u0440\u0430\u0441\u043a\u0438 \u0438 \u0436\u0438\u0432\u043e\u043f\u0438\u0441\u0438. \u041a\u0438\u0441\u0442\u0438 \u0434\u0435\u043b\u0430\u044e\u0442\u0441\u044f \u0438\u0437 \u0449\u0435\u0442\u0438\u043d\u044b \u0438 \u0445\u0432\u043e\u0441\u0442\u043e\u0432\u044b\u0445 \u0432\u043e\u043b\u043e\u0441\u043a\u043e\u0432 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0436\u0438\u0432\u043e\u0442\u043d\u044b\u0445.", - "created_at": "2018-01-31T14:57:37.751Z", - "update_at": "2018-01-31T14:57:37.751Z" - } - }, - { - "model": "course.material", - "pk": 3, - "fields": { - "title": "\u041a\u0440\u0430\u0441\u043a\u0438 \u0430\u043a\u0432\u0430\u0440\u0435\u043b\u044c\u043d\u044b\u0435", - "short_description": "\u0417\u0430\u0432\u043e\u0434 \u0445\u0443\u0434\u043e\u0436\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u043a\u0440\u0430\u0441\u043e\u043a \u00ab\u041d\u0435\u0432\u0441\u043a\u0430\u044f \u043f\u0430\u043b\u0438\u0442\u0440\u0430\u00bb \u0432\u044b\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u0430\u043a\u0432\u0430\u0440\u0435\u043b\u044c 80 \u043b\u0435\u0442, \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044f \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0441\u0442\u0432\u0430 \u043f\u0440\u043e\u0434\u0443\u043a\u0446\u0438\u0438 \u0432\u044b\u0441\u043e\u0447\u0430\u0439\u0448\u0435\u0433\u043e \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430. \u041f\u0440\u043e\u0432\u0435\u0440\u0435\u043d\u043d\u044b\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c \u0440\u0435\u0446\u0435\u043f\u0442\u0443\u0440\u044b, \u043e\u0442\u043b\u0430\u0436\u0435\u043d\u043d\u0430\u044f \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044f \u0438\u0437\u0433\u043e\u0442\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0430\u043a\u0432\u0430\u0440\u0435\u043b\u044c\u043d\u044b\u0435 \u043a\u0440\u0430\u0441\u043a\u0438 \u0432\u0438\u0437\u0438\u0442\u043d\u043e\u0439 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u043e\u0439 \u043f\u0440\u0435\u0434\u043f\u0440\u0438\u044f\u0442\u0438\u044f \u0432 \u0420\u043e\u0441\u0441\u0438\u0438", - "created_at": "2018-01-31T14:58:46.209Z", - "update_at": "2018-01-31T14:58:46.209Z" - } + { + "model": "course.course", + "pk": 1, + "fields": { + "author": 1, + "title": "Базовый курс для детей по основам иллюстрации", + "short_description": "Этот курс поможет детям узнать о том как из простых форм создавать веселый и харизматичных персонажей.", + "from_author": "", + "price": "1500.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 2, + "duration": 1, + "is_featured": false, + "status": 1, + "created_at": "2018-01-27T07:04:41.113Z", + "update_at": "2018-01-31T15:03:47.118Z", + "likes": [], + "materials": [] } -] \ No newline at end of file + }, + { + "model": "course.course", + "pk": 2, + "fields": { + "author": 1, + "title": "Базовый курс для детей по основам иллюстрации", + "short_description": "Этот курс поможет детям узнать о том как из простых форм создавать веселый и харизматичных персонажей.", + "from_author": "", + "price": "1900.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 1, + "duration": 1, + "is_featured": false, + "status": 1, + "created_at": "2018-01-27T07:09:03.437Z", + "update_at": "2018-01-31T15:03:47.115Z", + "likes": [], + "materials": [] + } + }, + { + "model": "course.course", + "pk": 3, + "fields": { + "author": 1, + "title": "Базовый курс для детей по основам иллюстрации", + "short_description": "Этот курс поможет детям узнать о том как из простых форм создавать веселый и харизматичных персонажей.", + "from_author": "", + "price": "100.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 9, + "duration": 1, + "is_featured": false, + "status": 1, + "created_at": "2018-01-27T07:09:03.442Z", + "update_at": "2018-01-31T15:03:47.112Z", + "likes": [], + "materials": [] + } + }, + { + "model": "course.course", + "pk": 4, + "fields": { + "author": 1, + "title": "Базовый курс для детей по основам иллюстрации", + "short_description": "Этот курс поможет детям узнать о том как из простых форм создавать веселый и харизматичных персонажей.", + "from_author": "", + "price": "400.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 8, + "duration": 1, + "is_featured": false, + "status": 1, + "created_at": "2018-01-27T07:09:03.445Z", + "update_at": "2018-01-31T15:03:47.108Z", + "likes": [], + "materials": [] + } + }, + { + "model": "course.course", + "pk": 5, + "fields": { + "author": 1, + "title": "Базовый курс для детей по основам иллюстрации", + "short_description": "Этот курс поможет детям узнать о том как из простых форм создавать веселый и харизматичных персонажей.", + "from_author": "", + "price": "1800.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 7, + "duration": 1, + "is_featured": false, + "status": 1, + "created_at": "2018-01-27T07:09:03.449Z", + "update_at": "2018-01-31T15:03:47.104Z", + "likes": [], + "materials": [] + } + }, + { + "model": "course.course", + "pk": 6, + "fields": { + "author": 1, + "title": "Базовый курс для детей по основам иллюстрации", + "short_description": "Этот курс поможет детям узнать о том как из простых форм создавать веселый и харизматичных персонажей.", + "from_author": "", + "price": "100.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 6, + "duration": 1, + "is_featured": false, + "status": 1, + "created_at": "2018-01-27T07:09:03.452Z", + "update_at": "2018-01-31T15:03:47.101Z", + "likes": [], + "materials": [] + } + }, + { + "model": "course.course", + "pk": 7, + "fields": { + "author": 1, + "title": "Базовый курс для детей по основам иллюстрации", + "short_description": "Этот курс поможет детям узнать о том как из простых форм создавать веселый и харизматичных персонажей.", + "from_author": "", + "price": "1600.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 5, + "duration": 1, + "is_featured": false, + "status": 1, + "created_at": "2018-01-27T07:09:03.455Z", + "update_at": "2018-01-31T15:03:47.097Z", + "likes": [], + "materials": [] + } + }, + { + "model": "course.course", + "pk": 8, + "fields": { + "author": 1, + "title": "Базовый курс для детей по основам иллюстрации", + "short_description": "Этот курс поможет детям узнать о том как из простых форм создавать веселый и харизматичных персонажей.", + "from_author": "", + "price": "1900.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 4, + "duration": 1, + "is_featured": false, + "status": 1, + "created_at": "2018-01-27T07:09:03.458Z", + "update_at": "2018-01-31T15:03:47.093Z", + "likes": [], + "materials": [] + } + }, + { + "model": "course.course", + "pk": 9, + "fields": { + "author": 1, + "title": "Базовый курс для детей по основам иллюстрации", + "short_description": "Этот курс поможет детям узнать о том как из простых форм создавать веселый и харизматичных персонажей.", + "from_author": "", + "price": "200.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 3, + "duration": 1, + "is_featured": false, + "status": 1, + "created_at": "2018-01-27T07:09:03.461Z", + "update_at": "2018-01-31T15:03:47.089Z", + "likes": [], + "materials": [] + } + }, + { + "model": "course.course", + "pk": 10, + "fields": { + "author": 1, + "title": "Базовый курс для детей по основам иллюстрации", + "short_description": "Этот курс поможет детям узнать о том как из простых форм создавать веселый и харизматичных персонажей.", + "from_author": "", + "price": "800.00", + "is_infinite": false, + "deferred_start_at": null, + "category": 2, + "duration": 1, + "is_featured": true, + "status": 1, + "created_at": "2018-01-27T07:09:03.464Z", + "update_at": "2018-01-31T15:03:47.086Z", + "likes": [], + "materials": [ + 1, + 2, + 3 + ] + } + }, + { + "model": "course.course", + "pk": 11, + "fields": { + "author": 1, + "title": "Базовый курс для детей по основам иллюстрации", + "short_description": "Этот курс поможет детям узнать о том как из простых форм создавать веселый и харизматичных персонажей.", + "from_author": "", + "price": "100.00", + "is_infinite": false, + "deferred_start_at": "2018-02-28T12:00:00Z", + "category": 1, + "duration": 1, + "is_featured": false, + "status": 1, + "created_at": "2018-01-27T07:09:03.467Z", + "update_at": "2018-01-31T15:03:47.080Z", + "likes": [], + "materials": [ + 1, + 2, + 3 + ] + } + }, + { + "model": "course.category", + "pk": 1, + "fields": { + "title": "персонаж" + } + }, + { + "model": "course.category", + "pk": 2, + "fields": { + "title": "акварель" + } + }, + { + "model": "course.category", + "pk": 3, + "fields": { + "title": "иллюстрация" + } + }, + { + "model": "course.category", + "pk": 4, + "fields": { + "title": "анимация" + } + }, + { + "model": "course.category", + "pk": 5, + "fields": { + "title": "пастель" + } + }, + { + "model": "course.category", + "pk": 6, + "fields": { + "title": "пластилин" + } + }, + { + "model": "course.category", + "pk": 7, + "fields": { + "title": "креативное мышление" + } + }, + { + "model": "course.category", + "pk": 8, + "fields": { + "title": "моторика" + } + }, + { + "model": "course.category", + "pk": 9, + "fields": { + "title": "живопись" + } + }, + { + "model": "course.lesson", + "pk": 1, + "fields": { + "title": "1 УРОК", + "short_description": "Выбираем сюжет, с которым мы будем работать на курсе и главного героя вашей истории. С этим героем мы будем работать на протяжении всего курса.", + "course": 11, + "created_at": "2018-01-31T15:06:14.830Z", + "update_at": "2018-01-31T15:06:14.830Z" + } + }, + { + "model": "course.lesson", + "pk": 2, + "fields": { + "title": "2 УРОК", + "short_description": "Собираем материал и экспериментируем с образом героя, используя полученный список характерных особенностей персонажа. По каждому признаку нужно будет собрать «референсы». Рисуем элементы образа в своем стиле.\\r\\n\\r\\nСобираем из них нашего персонажа. Выбираем самые удачные элементы, объединяем их в одном наброске.", + "course": 11, + "created_at": "2018-01-31T15:06:46.772Z", + "update_at": "2018-01-31T15:06:46.772Z" + } + }, + { + "model": "course.lesson", + "pk": 3, + "fields": { + "title": "3 УРОК", + "short_description": "Попробуем оживить персонажа. Выбираем 5 характерных действий для вашего героя и того сюжета, в котором он принимает участие, рисуем эскизы вашего персонажа в разных характерных для него позах и движении.\\r\\n\\r\\nВыделяем 5 характерных эмоций для героя, над которым работаем. По эмоциям подбираем референсы и стилизуем их в своем стиле.", + "course": 11, + "created_at": "2018-01-31T15:07:08.979Z", + "update_at": "2018-01-31T15:07:08.979Z" + } + }, + { + "model": "course.material", + "pk": 1, + "fields": { + "title": "Бумага акварельная", + "short_description": "Бумага для работы с акварелью имеет большое значение. Это объясняется тем, что акварель — краска прозрачная, а значит фактура бумаги будет достаточно сильно влиять на внешний вид красочного слоя.", + "created_at": "2018-01-31T14:55:48.394Z", + "update_at": "2018-01-31T14:55:48.394Z" + } + }, + { + "model": "course.material", + "pk": 2, + "fields": { + "title": "Кисточки для рисования", + "short_description": "Кисть — инструмент для покраски и живописи. Кисти делаются из щетины и хвостовых волосков различных животных.", + "created_at": "2018-01-31T14:57:37.751Z", + "update_at": "2018-01-31T14:57:37.751Z" + } + }, + { + "model": "course.material", + "pk": 3, + "fields": { + "title": "Краски акварельные", + "short_description": "Завод художественных красок «Невская палитра» выпускает акварель 80 лет, сохраняя традиции производства продукции высочайшего качества. Проверенные временем рецептуры, отлаженная технология изготовления сделали акварельные краски визитной карточкой предприятия в России", + "created_at": "2018-01-31T14:58:46.209Z", + "update_at": "2018-01-31T14:58:46.209Z" + } + } +] diff --git a/apps/school/fixtures/school_schedules.json b/apps/school/fixtures/school_schedules.json new file mode 100644 index 00000000..de85bf83 --- /dev/null +++ b/apps/school/fixtures/school_schedules.json @@ -0,0 +1,93 @@ +[ + { + "model": "school.schoolschedule", + "pk": 1, + "fields": { + "weekday": 1, + "title": "Акварель", + "description": "Сальвадор Дали родился в Испании 11 мая 1904 года в городе Фигерасе, провинция Жирона, в семье зажиточного нотариуса. По национальности был каталонцем, воспринимал себя в этом качестве и настаивал на этой своей особенности.", + "materials": "У него была сестра, Анна Мария Дали (исп. Anna Maria Dalí, 6 января 1908 — 16 мая 1989), и старший брат (12 октября 1901 — 1 августа 1903), который умер от менингита.", + "age": 0, + "month_price": "500.00", + "day_discount": "0.00" + } + }, + { + "model": "school.schoolschedule", + "pk": 2, + "fields": { + "weekday": 2, + "title": "Живопись", + "description": "Позднее в возрасте 5 лет на его могиле родители сказали Сальвадору, что он — реинкарнация своего старшего брата.", + "materials": "В детстве Дали был сообразительным, но заносчивым и неуправляемым ребёнком.", + "age": 0, + "month_price": "500.00", + "day_discount": "0.00" + } + }, + { + "model": "school.schoolschedule", + "pk": 3, + "fields": { + "weekday": 3, + "title": "Пластелин", + "description": "Однажды он затеял скандал на торговой площади ради леденца, вокруг собралась толпа, и полицейские попросили хозяина лавки открыть её во время сиесты и подарить мальчику сладость.", + "materials": "Он добивался своего капризами и симуляцией, всегда стремился выделиться и привлечь к себе внимание.", + "age": 0, + "month_price": "500.00", + "day_discount": "0.00" + } + }, + { + "model": "school.schoolschedule", + "pk": 4, + "fields": { + "weekday": 4, + "title": "Моторика", + "description": "Многочисленные комплексы[какие?] и фобии, например, страх перед кузнечиками[9], мешали ему включиться в обычную школьную жизнь, завести с детьми обычные связи дружбы и симпатии.", + "materials": "Но, как и любой человек, испытывая сенсорный голод, он искал эмоциональный контакт с детьми любыми способами, стараясь вжиться в их коллектив если не в роли товарища, то в любой другой роли, а точнее той единственной, на которую был способен, — в роли эпатажного и непослушного ребёнка, странного, чудаковатого, всегда поступающего вопреки чужим мнениям.", + "age": 0, + "month_price": "500.00", + "day_discount": "0.00" + } + }, + { + "model": "school.schoolschedule", + "pk": 5, + "fields": { + "weekday": 5, + "title": "Акварель", + "description": "Проигрывая в школьных азартных играх, он вёл себя так, будто выиграл, и торжествовал. Иногда без причины затевал драки.", + "materials": "Одноклассники относились к «странному» ребёнку довольно нетерпимо, использовали его страх перед кузнечиками, подсовывали ему за шиворот этих насекомых, чем доводили Сальвадора до истерики, о чём он позднее поведал в своей книге «Тайная жизнь Сальвадора Дали, рассказанная им самим».", + "age": 0, + "month_price": "500.00", + "day_discount": "0.00" + } + }, + { + "model": "school.schoolschedule", + "pk": 6, + "fields": { + "weekday": 6, + "title": "Анимация", + "description": "Обучаться изобразительному искусству Дали начал в муниципальной художественной школе. С 1914 по 1918 год воспитывался в Академии братьев ордена маристов в Фигерасе.", + "materials": "Одним из друзей детства был будущий футболист ФК «Барселона» Хосеп Самитьер. В 1916 году, с семьей Рамона Пишó, отправился на каникулы в город Кадакéс, где познакомился с современным искусством.", + "age": 0, + "month_price": "500.00", + "day_discount": "0.00" + } + }, + { + "model": "school.schoolschedule", + "pk": 7, + "fields": { + "weekday": 7, + "title": "Персонаж", + "description": "После прихода к власти Каудильо Фрáнко в 1936 году Дали ссорится с сюрреалистами, стоящими на левых позициях, и его исключают из группы.", + "materials": "В ответ Дали: «Сюрреализм — это я».", + "age": 0, + "month_price": "500.00", + "day_discount": "0.00" + } + } +] diff --git a/apps/user/fixtures/superuser.json b/apps/user/fixtures/superuser.json index eae528ed..a156f2aa 100644 --- a/apps/user/fixtures/superuser.json +++ b/apps/user/fixtures/superuser.json @@ -1,35 +1,35 @@ [ - { - "model": "user.user", - "pk": 1, - "fields": { - "password": "pbkdf2_sha256$100000$HoGxjmAQy4yo$3u+CrUiFtooddpg16OEUaSSjqUlwgFUBP1gt75hEoJs=", - "last_login": "2018-01-31T14:51:34Z", - "is_superuser": true, - "username": "admin@lil.city", - "first_name": "Lil", - "last_name": "City", - "is_staff": true, - "is_active": true, - "date_joined": "2018-01-28T08:41:19Z", - "email": "admin@lil.city", - "role": 2, - "gender": "n", - "country": "", - "city": "", - "about": "\u0417\u0430\u043a\u043e\u043d\u0447\u0438\u043b\u0430 \u041f\u0425\u0423 \u0438\u043c \u041a.\u0410.\u0421\u0430\u0432\u0438\u0446\u043a\u043e\u0433\u043e \u0445\u0443\u0434\u043e\u0436\u043d\u0438\u043a \u0442\u0435\u0430\u0442\u0440\u0430 \u0438 \u043a\u0438\u043d\u043e. \u0420\u0430\u0431\u043e\u0442\u0430\u043b\u0430 \u0441 \u043a\u0440\u0443\u043f\u043d\u0435\u0439\u0448\u0438\u043c\u0438 \u0440\u043e\u0441\u0441\u0438\u0439\u0441\u043a\u0438\u043c\u0438 \u0438 \u0437\u0430\u0440\u0443\u0431\u0435\u0436\u043d\u044b\u043c\u0438 \u0438\u0437\u0434\u0430\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u0430\u043c\u0438. \u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a \u0438 \u043f\u043e\u0431\u0435\u0434\u0438\u0442\u0435\u043b\u044c \u043c\u0435\u0436\u0434\u0443\u043d\u0430\u0440\u043e\u0434\u043d\u044b\u0445 \u0432\u044b\u0441\u0442\u0430\u0432\u043e\u043a. \u041e\u0441\u043d\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 \"Lil City\".", - "instagram": null, - "facebook": null, - "twitter": null, - "pinterest": null, - "youtube": null, - "vkontakte": null, - "fb_id": null, - "fb_data": {}, - "is_email_proved": true, - "photo": "", - "groups": [], - "user_permissions": [] - } + { + "model": "user.user", + "pk": 1, + "fields": { + "password": "pbkdf2_sha256$100000$HoGxjmAQy4yo$3u+CrUiFtooddpg16OEUaSSjqUlwgFUBP1gt75hEoJs=", + "last_login": "2018-01-31T14:51:34Z", + "is_superuser": true, + "username": "admin@lil.city", + "first_name": "Lil", + "last_name": "City", + "is_staff": true, + "is_active": true, + "date_joined": "2018-01-28T08:41:19Z", + "email": "admin@lil.city", + "role": 2, + "gender": "n", + "country": "", + "city": "", + "about": "Закончила ПХУ им К.А.Савицкого художник театра и кино. Работала с крупнейшими российскими и зарубежными издательствами. Участник и победитель международных выставок. Основатель компании \\"Lil City\\".", + "instagram": null, + "facebook": null, + "twitter": null, + "pinterest": null, + "youtube": null, + "vkontakte": null, + "fb_id": null, + "fb_data": {}, + "is_email_proved": true, + "photo": "", + "groups": [], + "user_permissions": [] } -] \ No newline at end of file + } +] From 65df4b8b21c67940ca5274ba3e5ecc609120a77a Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Fri, 2 Mar 2018 13:16:16 +0300 Subject: [PATCH 13/41] Fix superuser fixture --- apps/user/fixtures/superuser.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/user/fixtures/superuser.json b/apps/user/fixtures/superuser.json index a156f2aa..96362183 100644 --- a/apps/user/fixtures/superuser.json +++ b/apps/user/fixtures/superuser.json @@ -17,7 +17,7 @@ "gender": "n", "country": "", "city": "", - "about": "Закончила ПХУ им К.А.Савицкого художник театра и кино. Работала с крупнейшими российскими и зарубежными издательствами. Участник и победитель международных выставок. Основатель компании \\"Lil City\\".", + "about": "Закончила ПХУ им К.А.Савицкого художник театра и кино. Работала с крупнейшими российскими и зарубежными издательствами. Участник и победитель международных выставок. Основатель компании \"Lil City\".", "instagram": null, "facebook": null, "twitter": null, From 90486535cc406800cf8e1c54d571a10dd03eaffd Mon Sep 17 00:00:00 2001 From: Sanasol Date: Fri, 2 Mar 2018 19:20:30 +0700 Subject: [PATCH 14/41] styles, save loading etc --- web/src/js/modules/popup.js | 12 +++++++++--- web/src/sass/_common.sass | 4 ++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/web/src/js/modules/popup.js b/web/src/js/modules/popup.js index c6c5f86a..dddd1e32 100644 --- a/web/src/js/modules/popup.js +++ b/web/src/js/modules/popup.js @@ -50,7 +50,6 @@ $(document).ready(function () { var selectedWeekdays = {}; $(document).on('change', '[data-day]', function(){ - var days = ['', 'Понедельник', 'Вторник', 'Среда', 'Четврг', 'Пятница', 'Суббота', 'Воскресенье']; var weekday = $(this).data('day'); var price = $(this).data('price'); var discount = $(this).data('discount'); @@ -62,6 +61,11 @@ $(document).ready(function () { delete selectedWeekdays[weekday]; } + updateCart(); + }); + + function updateCart(){ + var days = ['', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота', 'Воскресенье']; var weekdays = [], daysText = [], price = 0, discount = 0; for(var i in selectedWeekdays) { price += parseInt(selectedWeekdays[i].price); @@ -77,11 +81,13 @@ $(document).ready(function () { text = price+'p.'; } $('.order_price_text').html(text); - $('.order__days').html(daysText.join(', ')); + $('.order__days').html((daysText.length) ? daysText.join(', '):'Ничего не выбрано'); var link = $('.but_btn_popup').data('link'); link = link+'?'+decodeURIComponent($.param({weekdays: weekdays}, true)); $('.but_btn_popup').attr('href', link); - }); + } + + updateCart(); }); \ No newline at end of file diff --git a/web/src/sass/_common.sass b/web/src/sass/_common.sass index f553d957..557e29ac 100755 --- a/web/src/sass/_common.sass +++ b/web/src/sass/_common.sass @@ -2648,6 +2648,8 @@ a.grey-link padding: 2px background-image: linear-gradient(-225deg, #FFE2EB 0%, #D8F5F5 100%) border-radius: 8px + &__days + min-height: 58px &__wrap padding: 20px 30px 30px background: white @@ -2681,6 +2683,8 @@ a.grey-link padding: 20px 20px 0 &__total margin-left: auto + del + color: #a0a0a0 .lock padding: 50px 60px 40px From 96fc4cfcb708bc969f5c16f13c659f3c4167e7b4 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Fri, 2 Mar 2018 18:20:05 +0300 Subject: [PATCH 15/41] Logging payment data --- apps/payment/views.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/payment/views.py b/apps/payment/views.py index dca03110..40b193f4 100644 --- a/apps/payment/views.py +++ b/apps/payment/views.py @@ -1,3 +1,6 @@ +import logging +import json + from django.contrib import messages from django.http import HttpResponse from django.shortcuts import redirect @@ -13,6 +16,8 @@ from apps.school.models import SchoolSchedule from .models import AuthorBalance, CoursePayment, SchoolPayment +logger = logging.getLogger('django') + class CourseBuyView(TemplateView): template_name = 'payment/paymentwall_widget.html' @@ -113,7 +118,9 @@ class PaymentwallCallbackView(View): payment = product_payment_class.objects.get(pk=payment_id) except product_payment_class.DoesNotExist: return HttpResponse(status=403) - + logger.info( + json.dumps(payment_raw_data), + ) payment.status = pingback.get_type() payment.data = payment_raw_data payment.save() From ad41d545d4c51cdfdf2706513bd67f29d83a7d76 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Fri, 2 Mar 2018 18:28:44 +0300 Subject: [PATCH 16/41] Add second paymenwall url --- project/urls.py | 1 + 1 file changed, 1 insertion(+) diff --git a/project/urls.py b/project/urls.py index 90aafb7e..f620359c 100644 --- a/project/urls.py +++ b/project/urls.py @@ -48,6 +48,7 @@ urlpatterns = [ path('lesson//', LessonView.as_view(), name='lesson'), path('lesson//comment', lessoncomment, name='lessoncomment'), path('payments/ping', PaymentwallCallbackView.as_view(), name='payment-ping'), + path('paymentwall/pingback', PaymentwallCallbackView.as_view(), name='payment-ping-second'), path('payments/success', TemplateView.as_view(template_name='payment/payment_success.html'), name='payment-success'), path('payments/error', TemplateView.as_view(template_name='payment/payment_error.html'), name='payment-error'), path('school/checkout', SchoolBuyView.as_view(), name='school-checkout'), From 1101fd97b51160260821afda4cc9d3465229f514 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Sun, 4 Mar 2018 10:37:12 +0300 Subject: [PATCH 17/41] Fix course template for free course --- apps/course/templates/course/course.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/course/templates/course/course.html b/apps/course/templates/course/course.html index 11ef33d1..b7b6bc88 100644 --- a/apps/course/templates/course/course.html +++ b/apps/course/templates/course/course.html @@ -25,7 +25,7 @@
Вернуться
- {% if not paid %} + {% if not paid and course.price %} {{ course.price|floatformat:"-2" }}₽ - {% if not paid %} + {% if not paid and course.price %} Date: Sun, 4 Mar 2018 10:39:37 +0300 Subject: [PATCH 18/41] Don't show course price for free course --- apps/course/templates/course/course.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/course/templates/course/course.html b/apps/course/templates/course/course.html index b7b6bc88..a928f119 100644 --- a/apps/course/templates/course/course.html +++ b/apps/course/templates/course/course.html @@ -106,6 +106,7 @@
{{ course.created_at | date:"d F Yг." }}
+ {% if course.price %}
@@ -114,6 +115,7 @@
{{ course.price|floatformat:"-2" }}₽
+ {% endif %}
@@ -378,6 +380,7 @@
{{ course.created_at | date:"d F Yг." }}
+ {% if course.price %}
@@ -386,6 +389,7 @@
{{ course.price|floatformat:"-2" }}₽
+ {% endif %} {% if not paid and course.price %}
Date: Sun, 4 Mar 2018 20:56:32 +0300 Subject: [PATCH 19/41] Upgrade django version --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 293862f7..925c03d5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ # Python-3.6 -Django==2.0.2 +Django==2.0.3 django-anymail[mailgun]==1.2 # paymentwall-python==1.0.7 git+https://github.com/ivlevdenis/paymentwall-python.git From 34cf929b605505ba949a2fe7dd56c027c7532a6a Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Sun, 4 Mar 2018 20:58:10 +0300 Subject: [PATCH 20/41] LIL-263,269,270,272,274 --- .../email/decline_withdrawal.html | 8 ++ .../migrations/0013_auto_20180304_1757.py | 36 +++++++++ apps/payment/models.py | 47 +++++++++++- apps/payment/views.py | 8 +- apps/user/fields.py | 25 +++++++ apps/user/forms.py | 7 ++ apps/user/templates/user/payment-history.html | 75 ++++++++++++++----- apps/user/views.py | 41 +++++++++- 8 files changed, 222 insertions(+), 25 deletions(-) create mode 100644 apps/notification/templates/notification/email/decline_withdrawal.html create mode 100644 apps/payment/migrations/0013_auto_20180304_1757.py create mode 100644 apps/user/fields.py diff --git a/apps/notification/templates/notification/email/decline_withdrawal.html b/apps/notification/templates/notification/email/decline_withdrawal.html new file mode 100644 index 00000000..814bc35d --- /dev/null +++ b/apps/notification/templates/notification/email/decline_withdrawal.html @@ -0,0 +1,8 @@ +{% extends "notification/email/_base.html" %} + +{% block content %} +

К сожалению вам отказано в выводе средств!

+
+

{{ author_balance.cause }}

+
+{% endblock content %} diff --git a/apps/payment/migrations/0013_auto_20180304_1757.py b/apps/payment/migrations/0013_auto_20180304_1757.py new file mode 100644 index 00000000..e0e16525 --- /dev/null +++ b/apps/payment/migrations/0013_auto_20180304_1757.py @@ -0,0 +1,36 @@ +# Generated by Django 2.0.2 on 2018-03-04 17:57 + +import django.core.validators +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('payment', '0012_auto_20180302_0740'), + ] + + operations = [ + migrations.AddField( + model_name='authorbalance', + name='card', + field=models.CharField(blank=True, max_length=20, null=True, validators=[django.core.validators.RegexValidator(regex='^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\\\d{3})\\d{11})$')]), + ), + migrations.AddField( + model_name='authorbalance', + name='created_at', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='authorbalance', + name='declined_send_at', + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name='authorbalance', + name='update_at', + field=models.DateTimeField(auto_now=True), + ), + ] diff --git a/apps/payment/models.py b/apps/payment/models.py index dc8426dd..6cf3402a 100644 --- a/apps/payment/models.py +++ b/apps/payment/models.py @@ -1,6 +1,7 @@ from django.db import models from django.contrib.auth import get_user_model from django.contrib.postgres.fields import ArrayField, JSONField +from django.core.validators import RegexValidator from constance import config from paymentwall import Pingback @@ -8,9 +9,12 @@ from polymorphic.models import PolymorphicModel from apps.course.models import Course from apps.school.models import SchoolSchedule +from apps.notification.utils import send_email User = get_user_model() +CREDIT_CARD_RE = r'^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\d{11})$' + class AuthorBalance(models.Model): IN = 0 @@ -35,11 +39,32 @@ class AuthorBalance(models.Model): status = models.PositiveSmallIntegerField('Статус', choices=STATUS_CHOICES, default=0) payment = models.OneToOneField('Payment', on_delete=models.CASCADE, null=True, blank=True, verbose_name='Платёж') cause = models.TextField('Причина отказа', null=True, blank=True) + card = models.CharField(max_length=20, validators=[RegexValidator(regex=CREDIT_CARD_RE)], null=True, blank=True) + + declined_send_at = models.DateTimeField(null=True, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + update_at = models.DateTimeField(auto_now=True) class Meta: verbose_name = 'Баланс' verbose_name_plural = 'Балансы' + def save(self, *args, **kwargs): + self.commission = self.calc_commission() + if self.type == self.OUT: + if self.status == self.DECLINED and not self.declined_send_at: + send_email( + 'Отказ вывода средств', + self.author.email, + 'notification/email/decline_withdrawal.html', + author_balance=self, + ) + self.declined_send_at = now() + super.save(*args, **kwargs) + + def calc_commission(self): + return self.amount * config.SERVICE_COMMISSION / 100 + class Payment(PolymorphicModel): PW_STATUS_CHOICES = ( @@ -66,6 +91,22 @@ class Payment(PolymorphicModel): def calc_commission(self): return self.amount * config.SERVICE_COMMISSION / 100 + def is_deliverable(self): + return self.status in [ + Pingback.PINGBACK_TYPE_REGULAR, + Pingback.PINGBACK_TYPE_GOODWILL, + Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED, + ] + + def is_under_review(self): + return self.status == Pingback.PINGBACK_TYPE_RISK_UNDER_REVIEW + + def is_cancelable(self): + return self.status in [ + Pingback.PINGBACK_TYPE_NEGATIVE, + Pingback.PINGBACK_TYPE_RISK_REVIEWED_DECLINED, + ] + class CoursePayment(Payment): course = models.ForeignKey(Course, on_delete=models.CASCADE, verbose_name='Курс', related_name='payments') @@ -83,11 +124,9 @@ class CoursePayment(Payment): author=self.course.author, amount=self.amount, payment=self, - commission=self.calc_commission(), ) else: author_balance.amount = self.amount - author_balance.commission = self.calc_commission() author_balance.save() @@ -100,6 +139,10 @@ class SchoolPayment(Payment): verbose_name = 'Платеж за школу' verbose_name_plural = 'Платежи за школу' + def __str__(self): + days = ', '.join([dict(SchoolSchedule.WEEKDAY_CHOICES).get(weekday, '') for weekday in sorted(self.weekdays)]) + return days + def save(self, *args, **kwargs): aggregate = SchoolSchedule.objects.filter( weekday__in=self.weekdays, diff --git a/apps/payment/views.py b/apps/payment/views.py index 40b193f4..4d195254 100644 --- a/apps/payment/views.py +++ b/apps/payment/views.py @@ -1,6 +1,8 @@ import logging import json +from datetime import timedelta + from django.contrib import messages from django.http import HttpResponse from django.shortcuts import redirect @@ -8,6 +10,7 @@ from django.views.generic import View, TemplateView from django.views.decorators.csrf import csrf_exempt from django.urls import reverse_lazy from django.utils.decorators import method_decorator +from django.utils.timezone import now from paymentwall import Pingback, Product, Widget @@ -123,10 +126,13 @@ class PaymentwallCallbackView(View): ) payment.status = pingback.get_type() payment.data = payment_raw_data + if pingback.is_deliverable() and product_type_name == 'school': + payment.date_start = now() + payment.date_end = now() + timedelta(days=30) payment.save() author_balance = getattr(payment, 'author_balance', None) - if author_balance: + if author_balance and author_balance.type == AuthorBalance.IN: if pingback.is_deliverable(): payment.author_balance.status = AuthorBalance.ACCEPTED elif pingback.is_under_review(): diff --git a/apps/user/fields.py b/apps/user/fields.py new file mode 100644 index 00000000..60063b86 --- /dev/null +++ b/apps/user/fields.py @@ -0,0 +1,25 @@ +import re + +from django import forms +from django.utils.translation import ugettext_lazy as _ + +CREDIT_CARD_RE = r'^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\d{11})$' + + +class CreditCardField(forms.CharField): + """ + Form field that validates credit card numbers. + """ + + default_error_messages = { + 'required': _(u'Номер карты обязателен.'), + 'invalid': _(u'Неверный номер карты.'), + } + + def clean(self, value): + value = value.replace(' ', '').replace('-', '') + if self.required and not value: + raise forms.utils.ValidationError(self.error_messages['required']) + if value and not re.match(CREDIT_CARD_RE, value): + raise forms.utils.ValidationError(self.error_messages['invalid']) + return value diff --git a/apps/user/forms.py b/apps/user/forms.py index 669a0fa0..2f75ef68 100644 --- a/apps/user/forms.py +++ b/apps/user/forms.py @@ -1,6 +1,8 @@ from django import forms from django.contrib.auth import get_user_model +from .fields import CreditCardField + User = get_user_model() @@ -47,3 +49,8 @@ class UserEditForm(forms.ModelForm): 'vkontakte', 'photo', ) + + +class WithdrawalForm(forms.Form): + amount = forms.DecimalField(required=True, min_value=2000) + card = CreditCardField(required=True) diff --git a/apps/user/templates/user/payment-history.html b/apps/user/templates/user/payment-history.html index f1daeb23..de91e85b 100644 --- a/apps/user/templates/user/payment-history.html +++ b/apps/user/templates/user/payment-history.html @@ -22,59 +22,98 @@ {% endif %} +{% if messages %} +
+
+ {% for message in messages %} +
{{ message }}
+ {% endfor %} +
+
+{% endif %} +{% if request.user.role == 1 or request.user.role == 2 %}
Вывести деньги со счета
-
+
{% csrf_token %}
На вашем счету {{ request.user.balance }} руб.
-
+
СУММА
- +
Размер выводимой суммы не должно быть менее 2000 рублей.
+ {% if form.amount.errors %} +
{{ form.amount.errors }}
+ {% endif %}
-
+
НОМЕР КРЕДИТНОЙ КАРТЫ *
- +
+ {% if form.card.errors %} +
{{ form.card.errors }}
+ {% endif %}
-
+
+{% endif %}
История платежей
- {% if request.user.role == 1 or request.user.role == 2 %} - {% for balance in request.user.balances.all %} -
-
{{balance.payment.course.title}}
-
{{balance.amount}}
-
Получено
-
- {% endfor %} - {% else %} {% for payment in request.user.payments.all %}
-
{{payment.course.title}}
+ {% if payment.course %} +
Курс. {{payment.course.title}}
+ {% else %} +
+ Школа. {% if payment.date_start and payment.date_end %}{{ payment.date_start }} - {{ payment.date_end }}{% endif %} + {{ payment }} +
+ {% endif %} + {% if payment.balance %} +
{{payment.balance.amount}}
+ {% else %}
{{payment.amount}}
-
Получено
+ {% endif %} + {% if payment.balance.type == 1 %} +
+ {% if payment.balance.status == 0 %} + Ожидается подтверждение выплаты + {% elif payment.balance.status == 1 %} + Выплачено + {% else %} + Выплата отменена + Причина: "{{ payment.balance.cause }} + {% endif %} +
+ {% else %} +
+ {% if payment.is_deliverable %} + Получено + {% elif payment.is_under_review %} + Ожидается подтверждение оплаты + {% else %} + Ошибка оплаты + {% endif %} +
+ {% endif %}
{% endfor %} - {% endif %}
diff --git a/apps/user/views.py b/apps/user/views.py index 0cb5b6c1..6d959069 100644 --- a/apps/user/views.py +++ b/apps/user/views.py @@ -1,9 +1,12 @@ from io import BytesIO from PIL import Image from os.path import splitext +from datetime import timedelta + from django.contrib.auth import login +from django.core.exceptions import ValidationError from django.shortcuts import render, reverse, redirect -from django.views.generic import DetailView, UpdateView, TemplateView +from django.views.generic import DetailView, UpdateView, TemplateView, FormView from django.contrib import messages from django.contrib.auth import get_user_model from django.contrib.auth.decorators import login_required, permission_required @@ -11,13 +14,14 @@ from django.contrib.auth.hashers import check_password, make_password from django.http import Http404 from django.urls import reverse_lazy from django.utils.decorators import method_decorator +from django.utils.timezone import now from apps.auth.tokens import verification_email_token from apps.course.models import Course -from apps.payment.models import CoursePayment +from apps.payment.models import AuthorBalance, CoursePayment from apps.notification.utils import send_email -from .forms import UserEditForm +from .forms import UserEditForm, WithdrawalForm User = get_user_model() @@ -31,6 +35,7 @@ def resend_email_verify(request): return redirect('user-edit-profile', request.user.id) +@method_decorator(login_required, name='dispatch') class UserView(DetailView): model = User template_name = 'user/profile.html' @@ -52,6 +57,7 @@ class UserView(DetailView): return context +@method_decorator(login_required, name='dispatch') class NotificationEditView(TemplateView): template_name = 'user/notification-settings.html' @@ -59,13 +65,40 @@ class NotificationEditView(TemplateView): return super().get(request) -class PaymentHistoryView(TemplateView): +@method_decorator(login_required, name='dispatch') +class PaymentHistoryView(FormView): template_name = 'user/payment-history.html' + form_class = WithdrawalForm def get(self, request, pk=None): return super().get(request) + def post(self, request, pk=None): + form = self.get_form() + if AuthorBalance.objects.filter(created_at__gte=now() - timedelta(days=30)).exists(): + messages.error(request, 'Запрос на вывод средств можно сделать только один раз в 30 дней.') + return self.form_invalid(form) + if form.is_valid(): + if request.user.balance < form.cleaned_data['amount']: + form.errors['amount'] = 'Сумма для вывода не может быть меньше средств на счету' + return self.form_invalid(form) + AuthorBalance.objects.create( + author=request.user, + type=AuthorBalance.OUT, + amount=form.cleaned_data['amount'], + status=AuthorBalance.PENDING, + card=form.cleaned_data['amount'], + ) + return self.form_valid(form) + else: + return self.form_invalid(form) + + def get_success_url(self): + success_url = reverse_lazy('user-edit-payments', args=[self.request.user.id]) + return success_url + +@method_decorator(login_required, name='dispatch') class UserEditView(UpdateView): model = User template_name = 'user/profile-settings.html' From 00961785d567e6c5ba9fd42c77641812af1a5ad6 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Mon, 5 Mar 2018 09:40:43 +0300 Subject: [PATCH 21/41] Fix save AuthorBalance --- apps/payment/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/payment/models.py b/apps/payment/models.py index 6cf3402a..4860b3c8 100644 --- a/apps/payment/models.py +++ b/apps/payment/models.py @@ -60,7 +60,7 @@ class AuthorBalance(models.Model): author_balance=self, ) self.declined_send_at = now() - super.save(*args, **kwargs) + super().save(*args, **kwargs) def calc_commission(self): return self.amount * config.SERVICE_COMMISSION / 100 From add4f2d8fb9903749a5538bf0ea524dba3121c49 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Mon, 5 Mar 2018 09:53:41 +0300 Subject: [PATCH 22/41] Backward django version --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 925c03d5..293862f7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ # Python-3.6 -Django==2.0.3 +Django==2.0.2 django-anymail[mailgun]==1.2 # paymentwall-python==1.0.7 git+https://github.com/ivlevdenis/paymentwall-python.git From 1e5ac2516d0bcdaf0fd32f978f54f81ebd815b0a Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Mon, 5 Mar 2018 11:11:09 +0300 Subject: [PATCH 23/41] Fix check payment status --- apps/course/views.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/course/views.py b/apps/course/views.py index 98fc5a4f..7cdcaa8e 100644 --- a/apps/course/views.py +++ b/apps/course/views.py @@ -1,3 +1,5 @@ +from paymentwall import Pingback + from django.contrib.auth import get_user_model from django.contrib.auth.decorators import login_required from django.db.models import Q @@ -191,11 +193,15 @@ class CourseView(DetailView): context['next'] = self.request.GET.get('next', None) context['paid'] = self.object.payments.filter( user=self.request.user, - authorbalance__status=AuthorBalance.ACCEPTED, + status__in=[ + Pingback.PINGBACK_TYPE_REGULAR, + Pingback.PINGBACK_TYPE_GOODWILL, + Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED, + ], ).exists() context['pending'] = self.object.payments.filter( user=self.request.user, - authorbalance__status=AuthorBalance.PENDING, + status=Pingback.PINGBACK_TYPE_RISK_UNDER_REVIEW, ).exists() return context From 44734d832839d31afd9229f983f02d1fcf36e7b8 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Mon, 5 Mar 2018 12:20:33 +0300 Subject: [PATCH 24/41] Update payment widget params --- apps/payment/views.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/payment/views.py b/apps/payment/views.py index 4d195254..fbc6cba2 100644 --- a/apps/payment/views.py +++ b/apps/payment/views.py @@ -40,16 +40,17 @@ class CourseBuyView(TemplateView): ) widget = Widget( str(request.user.id), - 'p1', + 'p1_1', [product], extra_params={ 'lang': 'ru', 'evaluation': 1, + 'demo': 1, + 'test_mode': 1, 'success_url': host + str(reverse_lazy('payment-success')), 'failure_url': host + str(reverse_lazy('payment-error')), } ) - return self.render_to_response(context={'widget': widget.get_html_code()}) @@ -79,16 +80,17 @@ class SchoolBuyView(TemplateView): ) widget = Widget( str(request.user.id), - 'p1', + 'p1_1', [product], extra_params={ 'lang': 'ru', 'evaluation': 1, + 'demo': 1, + 'test_mode': 1, 'success_url': host + str(reverse_lazy('payment-success')), 'failure_url': host + str(reverse_lazy('payment-error')), } ) - return self.render_to_response(context={'widget': widget.get_html_code()}) From 09e8ed56114ef8a2448a2ada7ebeb11c373dd166 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Mon, 5 Mar 2018 12:42:20 +0300 Subject: [PATCH 25/41] Check anonymous in cource detail view --- apps/course/views.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/apps/course/views.py b/apps/course/views.py index 7cdcaa8e..d04f6a2d 100644 --- a/apps/course/views.py +++ b/apps/course/views.py @@ -190,19 +190,20 @@ class CourseView(DetailView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['next'] = self.request.GET.get('next', None) - context['paid'] = self.object.payments.filter( - user=self.request.user, - status__in=[ - Pingback.PINGBACK_TYPE_REGULAR, - Pingback.PINGBACK_TYPE_GOODWILL, - Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED, - ], - ).exists() - context['pending'] = self.object.payments.filter( - user=self.request.user, - status=Pingback.PINGBACK_TYPE_RISK_UNDER_REVIEW, - ).exists() + if not self.request.user.is_anonymous: + context['next'] = self.request.GET.get('next', None) + context['paid'] = self.object.payments.filter( + user=self.request.user, + status__in=[ + Pingback.PINGBACK_TYPE_REGULAR, + Pingback.PINGBACK_TYPE_GOODWILL, + Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED, + ], + ).exists() + context['pending'] = self.object.payments.filter( + user=self.request.user, + status=Pingback.PINGBACK_TYPE_RISK_UNDER_REVIEW, + ).exists() return context def get_queryset(self): From 3ba9d395ca9ecdc32e6e428a6fcbf42d51ff21d0 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Mon, 5 Mar 2018 16:08:17 +0300 Subject: [PATCH 26/41] LIL-16 --- apps/content/tasks.py | 36 +++++++++++++++++++---------- project/settings.py | 12 ++++++++-- project/templates/lilcity/main.html | 24 +++++++++---------- requirements.txt | 4 +++- 4 files changed, 49 insertions(+), 27 deletions(-) diff --git a/apps/content/tasks.py b/apps/content/tasks.py index 898266bf..9f58dcaa 100644 --- a/apps/content/tasks.py +++ b/apps/content/tasks.py @@ -1,19 +1,31 @@ +import os import json -from time import sleep -from project.celery import app +import requests +import shutil + from constance import config -from InstagramAPI import InstagramAPI +from instagram.client import InstagramAPI +from project.celery import app +from time import sleep + +from django.conf import settings @app.task def retrieve_photos(): - instagram = InstagramAPI( - config.INSTAGRAM_CLIENT_LOGIN, - config.INSTAGRAM_CLIENT_PASSWORD, + api = InstagramAPI( + access_token=config.INSTAGRAM_CLIENT_ACCESS_TOKEN, + client_secret=config.INSTAGRAM_CLIENT_SECRET, ) - instagram.login() - sleep(1) - if instagram.isLoggedIn and instagram.getHashtagFeed(config.INSTAGRAM_RESULTS_TAG): - with open('s.json', 'w') as f: - f.write(json.dumps(instagram.LastJson)) - return instagram.LastJson + recent_media, next_ = api.user_recent_media(user_id='self', count=20) + path = os.path.join(settings.BASE_DIR, config.INSTAGRAM_RESULTS_PATH) + for idx, media in enumerate(recent_media): + try: + fname = os.path.join(path, f'{idx}.jpg') + r = requests.get(media.images['standard_resolution'].url, stream=True) + if r.status_code == 200: + with open(fname, 'wb') as f: + r.raw.decode_content = True + shutil.copyfileobj(r.raw, f) + except AttributeError: + pass diff --git a/project/settings.py b/project/settings.py index 2af789b3..c9b37278 100644 --- a/project/settings.py +++ b/project/settings.py @@ -12,6 +12,7 @@ https://docs.djangoproject.com/en/2.0/ref/settings/ import os from collections import OrderedDict +from datetime import timedelta # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -215,13 +216,20 @@ CELERY_BROKER_URL = 'redis://redis:6379/0' CELERY_RESULT_BACKEND = 'redis://redis:6379/1' CELERY_TASK_SERIALIZER = 'json' +CELERY_BEAT_SCHEDULE = { + 'retrieve_photos_from_instagram': { + 'task': 'apps.content.tasks.retrieve_photos', + 'schedule': timedelta(minutes=2), + 'args': (), + }, +} # Dynamic settings CONSTANCE_BACKEND = 'constance.backends.database.DatabaseBackend' CONSTANCE_CONFIG = OrderedDict(( - ('INSTAGRAM_CLIENT_LOGIN', ('', '')), - ('INSTAGRAM_CLIENT_PASSWORD', ('', '')), + ('INSTAGRAM_CLIENT_ACCESS_TOKEN', ('7145314808.f6fa114.ce354a5d876041fc9d3db04b0045587d', '')), + ('INSTAGRAM_CLIENT_SECRET', ('2334a921425140ccb180d145dcd35b25', '')), ('INSTAGRAM_RESULTS_TAG', ('#lil_акварель', 'Тэг результатов работ.')), ('INSTAGRAM_RESULTS_PATH', ('media/instagram/results/', 'Путь до результатов работ.')), ('SERVICE_COMMISSION', (10, 'Комиссия сервиса в процентах.')) diff --git a/project/templates/lilcity/main.html b/project/templates/lilcity/main.html index ece0bbcf..96de641c 100644 --- a/project/templates/lilcity/main.html +++ b/project/templates/lilcity/main.html @@ -192,42 +192,42 @@ diff --git a/requirements.txt b/requirements.txt index 293862f7..a2b3cb6e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ # Python-3.6 +requests==2.18.4 Django==2.0.2 django-anymail[mailgun]==1.2 # paymentwall-python==1.0.7 @@ -18,4 +19,5 @@ drf-yasg[validation]==1.4.0 django-silk==2.0.0 django-cors-headers==2.1.0 django-constance[database]==2.1.0 -InstagramAPI==1.0.2 +# python-instagram==1.3.2 +git+https://github.com/ivlevdenis/python-instagram.git From 0177d7f4af081c84549b8a3968b9821b412e536d Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Mon, 5 Mar 2018 16:15:09 +0300 Subject: [PATCH 27/41] Add instagram result folder --- media/instagram/results/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 media/instagram/results/.gitkeep diff --git a/media/instagram/results/.gitkeep b/media/instagram/results/.gitkeep new file mode 100644 index 00000000..e69de29b From b857215d2200baff9bf88b0e011a8b5977ec99b4 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Tue, 6 Mar 2018 10:08:37 +0300 Subject: [PATCH 28/41] Show auth popup on buy school if user not authenticated --- project/templates/lilcity/main.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/project/templates/lilcity/main.html b/project/templates/lilcity/main.html index 96de641c..a6a812c0 100644 --- a/project/templates/lilcity/main.html +++ b/project/templates/lilcity/main.html @@ -6,7 +6,11 @@
Первая онлайн-школа креативного мышления для детей! 5+
КУПИТЬ ДОСТУП ОТ 2000р. в мес. From 0a02cc8367a5554e9330e2ae4498daa297507293 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Tue, 6 Mar 2018 11:20:08 +0300 Subject: [PATCH 29/41] Fix index view --- project/urls.py | 14 +++----------- project/views.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 11 deletions(-) create mode 100644 project/views.py diff --git a/project/urls.py b/project/urls.py index f620359c..9a1c055b 100644 --- a/project/urls.py +++ b/project/urls.py @@ -24,13 +24,13 @@ from apps.course.views import ( lessoncomment, CourseEditView, CourseOnModerationView, ) -from apps.course.models import Course from apps.user.views import ( UserView, UserEditView, NotificationEditView, PaymentHistoryView, resend_email_verify, ) from apps.payment.views import CourseBuyView, PaymentwallCallbackView, SchoolBuyView -from apps.school.models import SchoolSchedule + +from .views import IndexView urlpatterns = [ path('admin/', admin.site.urls), @@ -61,15 +61,7 @@ urlpatterns = [ path('privacy', TemplateView.as_view(template_name="templates/lilcity/privacy_policy.html"), name='privacy'), path('terms', TemplateView.as_view(template_name="templates/lilcity/terms.html"), name='terms'), path('refund-policy', TemplateView.as_view(template_name="templates/lilcity/refund_policy.html"), name='refund_policy'), - path('', - TemplateView.as_view( - template_name="templates/lilcity/main.html", - extra_context={ - 'course_items': Course.objects.filter(status=Course.PUBLISHED)[:3], - 'school_schedules': SchoolSchedule.objects.all(), - }), - name='index' - ), + path('', IndexView.as_view(), name='index'), path('api/v1/', include(('api.v1.urls', 'api_v1'))), path('test', TemplateView.as_view(template_name="templates/lilcity/test.html"), name='test'), ] diff --git a/project/views.py b/project/views.py new file mode 100644 index 00000000..9dafb0a4 --- /dev/null +++ b/project/views.py @@ -0,0 +1,16 @@ +from django.views.generic import TemplateView + +from apps.course.models import Course +from apps.school.models import SchoolSchedule + + +class IndexView(TemplateView): + template_name = 'templates/lilcity/main.html' + + def get_context_data(self): + context = super().get_context_data() + context.update({ + 'course_items': Course.objects.filter(status=Course.PUBLISHED)[:3], + 'school_schedules': SchoolSchedule.objects.all(), + }) + return context From 6361b64f70104f650bec3fb7f4a4123c7c560275 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Tue, 6 Mar 2018 11:37:55 +0300 Subject: [PATCH 30/41] Fix save AuthorBalance --- apps/payment/models.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/payment/models.py b/apps/payment/models.py index 4860b3c8..7e396129 100644 --- a/apps/payment/models.py +++ b/apps/payment/models.py @@ -1,11 +1,12 @@ +from constance import config +from paymentwall import Pingback +from polymorphic.models import PolymorphicModel + from django.db import models from django.contrib.auth import get_user_model from django.contrib.postgres.fields import ArrayField, JSONField from django.core.validators import RegexValidator - -from constance import config -from paymentwall import Pingback -from polymorphic.models import PolymorphicModel +from django.utils.timezone import now from apps.course.models import Course from apps.school.models import SchoolSchedule From d8da3dd7e678fd953aaeec151201b28fae384ed0 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Tue, 6 Mar 2018 13:06:56 +0300 Subject: [PATCH 31/41] Turn off autocomplite on school buy form checkboxes --- project/templates/lilcity/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/templates/lilcity/index.html b/project/templates/lilcity/index.html index 6ec65f05..6ec88106 100644 --- a/project/templates/lilcity/index.html +++ b/project/templates/lilcity/index.html @@ -440,7 +440,7 @@
{% for school_schedule in school_schedules %}
-{% endblock content %} \ No newline at end of file +{% endblock content %} From 052ac0fb0e7c6e6329dfb346e0e17d26f19c7554 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Wed, 7 Mar 2018 11:40:49 +0300 Subject: [PATCH 36/41] Update yarn.lock --- web/yarn.lock | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/web/yarn.lock b/web/yarn.lock index 9758e20c..df8733cd 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -347,6 +347,14 @@ autoprefixer@^6.3.1, autoprefixer@^6.3.3: postcss "^5.2.16" postcss-value-parser "^3.2.3" +autosize-input@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/autosize-input/-/autosize-input-0.4.0.tgz#a5cdb43110e31f6a1d9fb695ccb747495af0bb5e" + +autosize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/autosize/-/autosize-4.0.0.tgz#7a0599b1ba84d73bd7589b0d9da3870152c69237" + aws-sign2@~0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" @@ -3282,6 +3290,10 @@ ieee754@^1.1.4: version "1.1.8" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" +ilyabirman-likely@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/ilyabirman-likely/-/ilyabirman-likely-2.3.0.tgz#4462becc5dedeb36b74bf4ba339a0ceab820785f" + immutable@3.8.2, immutable@^3.7.6: version "3.8.2" resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3" @@ -3979,6 +3991,10 @@ lodash.clonedeep@^4.3.2: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + lodash.defaults@^4.0.1: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" @@ -6768,6 +6784,13 @@ void-elements@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" +vue-autosize@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/vue-autosize/-/vue-autosize-1.0.2.tgz#d4159848a7c0f839d49dea45412ce472cdd3a48a" + dependencies: + autosize "^4.0.0" + autosize-input "^0.4.0" + vue-hot-reload-api@^2.2.0: version "2.2.4" resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.2.4.tgz#683bd1d026c0d3b3c937d5875679e9a87ec6cd8f" @@ -6823,6 +6846,10 @@ vuejs-datepicker@^0.9.25: version "0.9.26" resolved "https://registry.yarnpkg.com/vuejs-datepicker/-/vuejs-datepicker-0.9.26.tgz#63e8ab8f3725663f3f90543545d3c097b8ff8f43" +vuelidate@^0.6.1: + version "0.6.2" + resolved "https://registry.yarnpkg.com/vuelidate/-/vuelidate-0.6.2.tgz#b417f5367407b047a2548119b25db56fe2bd5753" + warning@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c" From 3c1242cbb847e7170b84254f1245b86244434101 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Wed, 7 Mar 2018 11:41:23 +0300 Subject: [PATCH 37/41] Update school data in user profile --- apps/course/templates/course/school.html | 68 ++++++++++++++---------- apps/payment/views.py | 20 ++++++- apps/user/views.py | 31 ++++++++++- 3 files changed, 86 insertions(+), 33 deletions(-) diff --git a/apps/course/templates/course/school.html b/apps/course/templates/course/school.html index e0eaecf7..ebca264c 100644 --- a/apps/course/templates/course/school.html +++ b/apps/course/templates/course/school.html @@ -1,34 +1,44 @@ +{% load static %} +{% load rupluralize from plural %} +{% if school_payment %}
-
-
-
-
Январь
-
Осталось 14 дней
-
- -
-
+
+
+
+
+ Дата окончания: +

{{ school_payment.date_end }}

- -
- ПОСМОТРЕТЬ ПРЕДЫДУЩИЕ УРОКИ +
Осталось: {{ school_days_left | rupluralize:"день,дня,дней" }}
+
+
+
-
- {% for course in course_items %} -
-
Понедельник
-
Пластилиновая живопись.
-
-

Научимся смешивать цвета, получать красивые оттенки и создавать картины из пластилина разными техниками. Узнаем как хранить и ухаживать за такими работами.

-
-
- {% endfor %} + + +
+
+ {% for school_schedule in school_schedules %} +
+
{{ school_schedule }}
+
{{ school_schedule.title }}
+
+

{{ school_schedule.description }}

+
-
\ No newline at end of file + {% endfor %} +
+
+{% endif %} diff --git a/apps/payment/views.py b/apps/payment/views.py index fbc6cba2..66b3fe03 100644 --- a/apps/payment/views.py +++ b/apps/payment/views.py @@ -129,8 +129,24 @@ class PaymentwallCallbackView(View): payment.status = pingback.get_type() payment.data = payment_raw_data if pingback.is_deliverable() and product_type_name == 'school': - payment.date_start = now() - payment.date_end = now() + timedelta(days=30) + school_payment = SchoolPayment.objects.filter( + user=self.object, + date_start__lte=now(), + date_end__gt=now(), + status__in=[ + Pingback.PINGBACK_TYPE_REGULAR, + Pingback.PINGBACK_TYPE_GOODWILL, + Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED, + ], + ).last() + if school_payment: + date_start = school_payment.date_end + timedelta(days=1) + date_end = date_start + timedelta(days=30) + else: + date_start = now() + date_end = now() + timedelta(days=30) + payment.date_start = date_start + payment.date_end = date_end payment.save() author_balance = getattr(payment, 'author_balance', None) diff --git a/apps/user/views.py b/apps/user/views.py index 6d959069..533d19ad 100644 --- a/apps/user/views.py +++ b/apps/user/views.py @@ -1,7 +1,10 @@ +import arrow + from io import BytesIO from PIL import Image from os.path import splitext from datetime import timedelta +from paymentwall import Pingback from django.contrib.auth import login from django.core.exceptions import ValidationError @@ -18,8 +21,9 @@ from django.utils.timezone import now from apps.auth.tokens import verification_email_token from apps.course.models import Course -from apps.payment.models import AuthorBalance, CoursePayment from apps.notification.utils import send_email +from apps.school.models import SchoolSchedule +from apps.payment.models import AuthorBalance, CoursePayment, SchoolPayment from .forms import UserEditForm, WithdrawalForm @@ -52,8 +56,31 @@ class UserView(DetailView): author=self.object, status=Course.DRAFT ) context['paid'] = Course.objects.filter( - payments__in=CoursePayment.objects.filter(user=self.object), + payments__in=CoursePayment.objects.filter( + user=self.object, + status__in=[ + Pingback.PINGBACK_TYPE_REGULAR, + Pingback.PINGBACK_TYPE_GOODWILL, + Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED, + ], + ), ).distinct() + school_payment = SchoolPayment.objects.filter( + user=self.object, + date_start__lte=now(), + date_end__gt=now(), + status__in=[ + Pingback.PINGBACK_TYPE_REGULAR, + Pingback.PINGBACK_TYPE_GOODWILL, + Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED, + ], + ).last() + context['school_payment'] = school_payment + if school_payment and school_payment.date_end: + context['school_days_left'] = (school_payment.date_end - now().date()).days + context['school_schedules'] = SchoolSchedule.objects.filter( + weekday__in=school_payment.weekdays if school_payment else [], + ) return context From f73e7f4f80799811c14cf57b9c111244e248f3bf Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Wed, 7 Mar 2018 11:47:49 +0300 Subject: [PATCH 38/41] Update PaymentwallCallbackView --- apps/payment/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/payment/views.py b/apps/payment/views.py index 66b3fe03..f22aefc7 100644 --- a/apps/payment/views.py +++ b/apps/payment/views.py @@ -130,7 +130,7 @@ class PaymentwallCallbackView(View): payment.data = payment_raw_data if pingback.is_deliverable() and product_type_name == 'school': school_payment = SchoolPayment.objects.filter( - user=self.object, + user=payment.user, date_start__lte=now(), date_end__gt=now(), status__in=[ From a031aae945a9feaa3b7fbe557cdcd90a3680115b Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Wed, 7 Mar 2018 12:12:14 +0300 Subject: [PATCH 39/41] Allow edit payment status --- .../migrations/0015_auto_20180307_0911.py | 18 ++++++++++++++++++ apps/payment/models.py | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 apps/payment/migrations/0015_auto_20180307_0911.py diff --git a/apps/payment/migrations/0015_auto_20180307_0911.py b/apps/payment/migrations/0015_auto_20180307_0911.py new file mode 100644 index 00000000..6c24eb32 --- /dev/null +++ b/apps/payment/migrations/0015_auto_20180307_0911.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.2 on 2018-03-07 09:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('payment', '0014_auto_20180307_0617'), + ] + + operations = [ + migrations.AlterField( + model_name='payment', + name='status', + field=models.PositiveSmallIntegerField(choices=[(0, 'regular'), (1, 'goodwill'), (2, 'negative'), (200, 'risk under review'), (201, 'risk reviewed accepted'), (202, 'risk reviewed declined'), (203, 'risk authorization voided'), (12, 'subscription cancelation'), (13, 'subscription expired'), (14, 'subscription payment failed')], null=True, verbose_name='Статус платежа'), + ), + ] diff --git a/apps/payment/models.py b/apps/payment/models.py index dca1a362..e7bc6c3c 100644 --- a/apps/payment/models.py +++ b/apps/payment/models.py @@ -82,7 +82,7 @@ class Payment(PolymorphicModel): ) user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='Пользователь', related_name='payments') amount = models.DecimalField('Итого', max_digits=8, decimal_places=2, default=0, editable=False) - status = models.PositiveSmallIntegerField('Статус платежа', choices=PW_STATUS_CHOICES, null=True, editable=False) + status = models.PositiveSmallIntegerField('Статус платежа', choices=PW_STATUS_CHOICES, null=True) data = JSONField('Данные платежа от провайдера', default={}, editable=False) created_at = models.DateTimeField(auto_now_add=True) update_at = models.DateTimeField(auto_now=True) From c6a738c78631df7319a71dc56b6df2906abdfb2a Mon Sep 17 00:00:00 2001 From: Sanasol Date: Wed, 7 Mar 2018 16:32:20 +0700 Subject: [PATCH 40/41] style fixes, etc --- apps/user/templates/user/payment-history.html | 85 +++++++++++-------- web/package-lock.json | 5 ++ web/package.json | 1 + web/src/js/modules/common.js | 3 +- web/src/sass/_common.sass | 6 ++ 5 files changed, 62 insertions(+), 38 deletions(-) diff --git a/apps/user/templates/user/payment-history.html b/apps/user/templates/user/payment-history.html index de91e85b..f96b20fb 100644 --- a/apps/user/templates/user/payment-history.html +++ b/apps/user/templates/user/payment-history.html @@ -75,50 +75,61 @@
История платежей
- {% for payment in request.user.payments.all %} -
- {% if payment.course %} -
Курс. {{payment.course.title}}
- {% else %} -
- Школа. {% if payment.date_start and payment.date_end %}{{ payment.date_start }} - {{ payment.date_end }}{% endif %} - {{ payment }} -
- {% endif %} - {% if payment.balance %} -
{{payment.balance.amount}}
- {% else %} -
{{payment.amount}}
- {% endif %} - {% if payment.balance.type == 1 %} -
- {% if payment.balance.status == 0 %} - Ожидается подтверждение выплаты - {% elif payment.balance.status == 1 %} - Выплачено + {% if request.user.payments.all|length %} + {% for payment in request.user.payments.all %} +
+ {% if payment.course %} +
Курс. {{payment.course.title}}
{% else %} - Выплата отменена - Причина: "{{ payment.balance.cause }} +
+ Школа. {% if payment.date_start and payment.date_end %}{{ payment.date_start }} - {{ payment.date_end }}{% endif %} + {{ payment }} +
{% endif %} -
- {% else %} -
- {% if payment.is_deliverable %} - Получено - {% elif payment.is_under_review %} - Ожидается подтверждение оплаты + {% if payment.balance %} +
{{payment.balance.amount}}
+ {% else %} +
{{payment.amount}}
+ {% endif %} + {% if payment.balance.type == 1 %} +
+ {% if payment.balance.status == 0 %} + Ожидается подтверждение выплаты + {% elif payment.balance.status == 1 %} + Выплачено + {% else %} + Выплата отменена + Причина: "{{ payment.balance.cause }} + {% endif %} +
{% else %} - Ошибка оплаты +
+ {% if payment.is_deliverable %} + Получено + {% elif payment.is_under_review %} + Ожидается подтверждение оплаты + {% else %} + Ошибка оплаты + {% endif %} +
{% endif %}
- {% endif %} -
- {% endfor %} -
-
- + {% endfor %} + {% else %} + Оплат еще не было + {% endif %}
+ + +
+ {% endblock content %} + +{% block foot %} + +{% endblock foot %} \ No newline at end of file diff --git a/web/package-lock.json b/web/package-lock.json index 17fe64e6..c98f2720 100755 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -6161,6 +6161,11 @@ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, + "inputmask": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/inputmask/-/inputmask-3.3.11.tgz", + "integrity": "sha1-FCHJSuKMPc0bTSYze1CLs0mY4tg=" + }, "interpret": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", diff --git a/web/package.json b/web/package.json index ad25f0bd..96794b84 100755 --- a/web/package.json +++ b/web/package.json @@ -57,6 +57,7 @@ "babel-polyfill": "^6.26.0", "history": "^4.7.2", "ilyabirman-likely": "^2.3.0", + "inputmask": "^3.3.11", "jquery": "^3.3.1", "lodash.debounce": "^4.0.8", "moment": "^2.20.1", diff --git a/web/src/js/modules/common.js b/web/src/js/modules/common.js index 5eea0db6..ecbd4dee 100644 --- a/web/src/js/modules/common.js +++ b/web/src/js/modules/common.js @@ -1,6 +1,7 @@ import $ from 'jquery'; +import Inputmask from "inputmask"; import SmoothScroll from 'smooth-scroll/dist/js/smooth-scroll'; - +window.Inputmask = Inputmask; $(document).ready(function () { // Добавляем заголовок X-CSRFToken для всех AJAX запросов JQuery. $.ajaxSetup({ diff --git a/web/src/sass/_common.sass b/web/src/sass/_common.sass index 557e29ac..60ca347d 100755 --- a/web/src/sass/_common.sass +++ b/web/src/sass/_common.sass @@ -2875,6 +2875,12 @@ a.grey-link &__cell padding: 0 10px font-size: 13px + &__success + color: $green + &__pending + color: $gray + &__error + color: $pink &:first-child +fb font-size: 12px From fb1e4d878635377ccba1850ee57d5e029d19101f Mon Sep 17 00:00:00 2001 From: Sanasol Date: Wed, 7 Mar 2018 16:38:08 +0700 Subject: [PATCH 41/41] section_gray payments --- apps/user/templates/user/payment-history.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/user/templates/user/payment-history.html b/apps/user/templates/user/payment-history.html index f96b20fb..4c9a34b7 100644 --- a/apps/user/templates/user/payment-history.html +++ b/apps/user/templates/user/payment-history.html @@ -32,7 +32,7 @@
{% endif %} {% if request.user.role == 1 or request.user.role == 2 %} -
+
Вывести деньги со счета
{% csrf_token %} @@ -70,7 +70,7 @@
{% endif %} -
+
История платежей