diff --git a/api/v1/serializers/course.py b/api/v1/serializers/course.py index 781af25e..e393aac9 100644 --- a/api/v1/serializers/course.py +++ b/api/v1/serializers/course.py @@ -128,6 +128,7 @@ class CourseCreateSerializer(DispatchContentMixin, 'from_author', 'cover', 'price', + 'old_price', 'age', 'is_infinite', 'deferred_start_at', @@ -280,6 +281,7 @@ class CourseSerializer(DynamicFieldsMixin, serializers.ModelSerializer): 'from_author', 'cover', 'price', + 'old_price', 'age', 'is_infinite', 'deferred_start_at', diff --git a/api/v1/serializers/payment.py b/api/v1/serializers/payment.py index 8d50f217..7c48cf46 100644 --- a/api/v1/serializers/payment.py +++ b/api/v1/serializers/payment.py @@ -3,7 +3,7 @@ from rest_framework import serializers from apps.payment.models import ( AuthorBalance, Payment, CoursePayment, SchoolPayment, - GiftCertificatePayment) + GiftCertificatePayment, UserBonus,) from .user import UserSerializer from .course import CourseSerializer @@ -200,3 +200,68 @@ class GiftCertificatePaymentSerializer(serializers.ModelSerializer): 'created_at', 'update_at', ) + + +class UserBonusCreateSerializer(serializers.ModelSerializer): + class Meta: + model = UserBonus + fields = ( + 'user', 'amount', 'payment', 'referral', 'created_at', 'is_service', 'action_name', + ) + read_only_fields = ( + 'id', + 'created_at', + ) + + def to_representation(self, instance): + return UserBonusSerializer(instance, context=self.context).to_representation(instance) + + +class UserBonusSerializer(serializers.ModelSerializer): + user = UserSerializer() + payment = serializers.SerializerMethodField() + referral = serializers.SerializerMethodField() + + class Meta: + model = UserBonus + fields = ( + 'user', 'amount', 'payment', 'referral', 'created_at', 'is_service', 'action_name', + ) + read_only_fields = ( + 'id', + 'user', + 'created_at', + ) + + def get_payment(self, instance): + try: + p = instance.payment + except Exception: + return None + if not p: + return None + data = { + 'id': p.id, + 'created_at': p.created_at, + 'amount': p.amount, + 'data': p.data, + 'status': p.status, + } + if isinstance(instance.payment, CoursePayment): + data['course'] = { + 'id': p.course.id, + 'title': p.course.title, + } + return data + + def get_referral(self, instance): + try: + r = instance.referral + except Exception: + return None + if not r: + return None + return { + 'id': r.id, + 'referral': UserSerializer(instance=r.referral).data, + } diff --git a/api/v1/urls.py b/api/v1/urls.py index b86acbbc..07baaaf1 100644 --- a/api/v1/urls.py +++ b/api/v1/urls.py @@ -19,7 +19,7 @@ from .views import ( SchoolScheduleViewSet, LiveLessonViewSet, PaymentViewSet, ObjectCommentsViewSet, ContestViewSet, ContestWorkViewSet, - AuthorBalanceUsersViewSet, CaptureEmail, FAQViewSet, UserGalleryViewSet) + AuthorBalanceUsersViewSet, CaptureEmail, FAQViewSet, UserGalleryViewSet, BonusesViewSet) router = DefaultRouter() router.register(r'author-requests', AuthorRequestViewSet, base_name='author-requests') @@ -43,10 +43,9 @@ router.register(r'galleries', GalleryViewSet, base_name='galleries') router.register(r'gallery-images', GalleryImageViewSet, base_name='gallery-images') router.register(r'faq', FAQViewSet, base_name='faq') router.register(r'school-schedules', SchoolScheduleViewSet, base_name='school-schedules') - +router.register(r'bonuses', BonusesViewSet, base_name='bonuses') router.register(r'users', UserViewSet, base_name='users') router.register(r'user-gallery', UserGalleryViewSet, base_name='user-gallery') - router.register(r'contests', ContestViewSet, base_name='contests') router.register(r'contest-works', ContestWorkViewSet, base_name='contest_works') diff --git a/api/v1/views.py b/api/v1/views.py index 3164c00a..c2b43019 100644 --- a/api/v1/views.py +++ b/api/v1/views.py @@ -39,7 +39,7 @@ from .serializers.school import ( ) from .serializers.payment import ( AuthorBalanceSerializer, AuthorBalanceCreateSerializer, - PaymentSerializer, + PaymentSerializer, UserBonusSerializer, UserBonusCreateSerializer, CoursePaymentCreateSerializer, SchoolPaymentCreateSerializer) from .serializers.user import ( AuthorRequestSerializer, @@ -68,7 +68,7 @@ from apps.content.models import ( Contest, ContestWork, FAQ) from apps.payment.models import ( AuthorBalance, Payment, - CoursePayment, SchoolPayment, + CoursePayment, SchoolPayment, UserBonus, ) from apps.school.models import SchoolSchedule, LiveLesson from apps.user.models import AuthorRequest @@ -724,7 +724,37 @@ class CaptureEmail(views.APIView): return Response({'status': 'ok'}) - class FAQViewSet(ExtendedModelViewSet): queryset = FAQ.objects.all() serializer_class = FAQSerializer + + +class BonusesViewSet(ExtendedModelViewSet): + queryset = UserBonus.objects.all() + serializer_class = UserBonusCreateSerializer + serializer_class_map = { + 'list': UserBonusSerializer, + } + permission_classes = (IsAdmin,) + filter_fields = ('user', 'referral', 'payment', 'is_service', 'action_name') + search_fields = ( + 'action_name', + 'user__email', + 'user__first_name', + 'user__last_name', + 'referral__referral__email', + 'referral__referral__first_name', + 'referral__referral__last_name', + ) + + def list(self, request, *args, **kwargs): + queryset = self.filter_queryset(self.get_queryset()) + + if request.query_params.get('page'): + page = self.paginate_queryset(queryset) + if page is not None: + serializer = self.get_serializer(page, many=True) + return self.get_paginated_response(serializer.data) + + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data) diff --git a/apps/course/migrations/0047_course_old_price.py b/apps/course/migrations/0047_course_old_price.py new file mode 100644 index 00000000..5c389496 --- /dev/null +++ b/apps/course/migrations/0047_course_old_price.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.7 on 2019-01-13 23:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0046_auto_20181009_2334'), + ] + + operations = [ + migrations.AddField( + model_name='course', + name='old_price', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='Старая цена курса'), + ), + ] diff --git a/apps/course/models.py b/apps/course/models.py index 6272a318..42a1dd81 100644 --- a/apps/course/models.py +++ b/apps/course/models.py @@ -80,6 +80,10 @@ class Course(BaseModel, DeactivatedMixin): verbose_name='Обложка курса', on_delete=models.SET_NULL, null=True, blank=True, ) + old_price = models.DecimalField( + 'Старая цена курса', + max_digits=10, decimal_places=2, null=True, blank=True + ) price = models.DecimalField( 'Цена курса', help_text='Если цена не выставлена, то курс бесплатный', max_digits=10, decimal_places=2, null=True, blank=True @@ -132,7 +136,7 @@ class Course(BaseModel, DeactivatedMixin): return self.get_absolute_url() def get_absolute_url(self): - return reverse_lazy('course', args=[self.id]) + return reverse_lazy('course', args=[self.slug or self.id]) @property def is_free(self): diff --git a/apps/course/templates/course/_items.html b/apps/course/templates/course/_items.html index ff72a87d..a5bfa123 100644 --- a/apps/course/templates/course/_items.html +++ b/apps/course/templates/course/_items.html @@ -7,7 +7,7 @@ data-course data-course-id={{ course.id }} {% if course.is_deferred_start and course.status == 2 %}data-future-course data-future-course-time={{ course.deferred_start_at.timestamp }}{% endif %} > - + {% thumbnail course.cover.image "300x200" crop="center" as im %} {% empty %} @@ -50,10 +50,13 @@ {{ course.category | upper }} {% if not course.is_free %} + {% if course.old_price %} +
{{ course.old_price|floatformat:"-2" }}₽
+ {% endif %}
{{ course.price|floatformat:"-2" }}₽
{% endif %} - {{ course.title }} + {{ course.title }}
{{ course.short_description | safe | linebreaks | truncatechars_html:300 }}
diff --git a/apps/course/templates/course/course.html b/apps/course/templates/course/course.html index 97f11f84..94fd1938 100644 --- a/apps/course/templates/course/course.html +++ b/apps/course/templates/course/course.html @@ -114,13 +114,16 @@
{{ course.duration | rupluralize:"день,дня,дней" }}
{% if course.price %} -
+
-
{{ course.price|floatformat:"-2" }}₽
+
+ {% if course.old_price %}{{ course.old_price|floatformat:"-2" }}₽{% endif %} + {{ course.price|floatformat:"-2" }}₽ +
{% endif %}
@@ -142,7 +145,7 @@
- Описание курса + Описание курса {% if request.user.is_authenticated %} {% if course.author == request.user and request.user.role >= request.user.AUTHOR_ROLE %}
- Описание курса + Описание курса {% if course.author == request.user and request.user.role >= request.user.AUTHOR_ROLE %}
Вы успешно приобрели курс!
diff --git a/apps/payment/templates/payment/payment_success.html b/apps/payment/templates/payment/payment_success.html index 9d256634..fe3c6a15 100644 --- a/apps/payment/templates/payment/payment_success.html +++ b/apps/payment/templates/payment/payment_success.html @@ -11,7 +11,7 @@ {% if course %}
Вы успешно приобрели курс!
- ПЕРЕЙТИ К КУРСУ + ПЕРЕЙТИ К КУРСУ
{% endif %} {% if gift_certificate %} diff --git a/apps/school/models.py b/apps/school/models.py index 4eb71350..38889f44 100644 --- a/apps/school/models.py +++ b/apps/school/models.py @@ -142,8 +142,12 @@ class LiveLesson(BaseModel, DeactivatedMixin): self.date = (datetime.combine(self.date, now().time()) + timedelta(days=1)).date() super().save(*args, **kwargs) + @property + def url(self): + return self.get_absolute_url() + def get_absolute_url(self): - return reverse_lazy('school:lesson-detail', args=[str(self.id)]) + return reverse_lazy('school:lesson-detail', kwargs={'lesson_date': self.date.strftime('%d-%m-%y')}) def stream_index(self): return self.stream.split('/')[-1] diff --git a/apps/school/templates/blocks/open_lesson.html b/apps/school/templates/blocks/open_lesson.html index 46999c7f..1aefa579 100644 --- a/apps/school/templates/blocks/open_lesson.html +++ b/apps/school/templates/blocks/open_lesson.html @@ -1,4 +1,4 @@ смотреть урок diff --git a/apps/school/templates/school/livelessons_list.html b/apps/school/templates/school/livelessons_list.html index 237072b0..161a6629 100644 --- a/apps/school/templates/school/livelessons_list.html +++ b/apps/school/templates/school/livelessons_list.html @@ -11,7 +11,7 @@ {% for livelesson in livelesson_list %}
- + diff --git a/apps/school/templates/summer/open_lesson.html b/apps/school/templates/summer/open_lesson.html index abb8a9a2..793fb589 100644 --- a/apps/school/templates/summer/open_lesson.html +++ b/apps/school/templates/summer/open_lesson.html @@ -1,4 +1,4 @@ подробнее diff --git a/apps/school/urls.py b/apps/school/urls.py index 59973c6d..9ac1d078 100644 --- a/apps/school/urls.py +++ b/apps/school/urls.py @@ -1,4 +1,4 @@ -from django.urls import path, include +from django.urls import path, re_path from .views import ( LiveLessonsView, LiveLessonEditView, @@ -12,5 +12,6 @@ urlpatterns = [ path('lessons/', LiveLessonsView.as_view(), name='lessons'), path('lessons/create', LiveLessonEditView.as_view(), name='lessons-create'), path('lessons//edit', LiveLessonEditView.as_view(), name='lessons-edit'), - path('lessons//', LiveLessonsDetailView.as_view(), name='lesson-detail'), + path('lessons//', LiveLessonsDetailView.as_view(), name='lesson-detail-id'), + re_path(r'(?P\d+\-\d+\-\d+)', LiveLessonsDetailView.as_view(), name='lesson-detail'), ] diff --git a/apps/school/views.py b/apps/school/views.py index b58869b5..9081acd6 100644 --- a/apps/school/views.py +++ b/apps/school/views.py @@ -61,8 +61,11 @@ class LiveLessonsDetailView(DetailView): model = LiveLesson template_name = 'school/livelesson_detail.html' - def get(self, request, pk=None): - self.object = self.get_object() + def get(self, request, pk=None, lesson_date=None): + if pk: + self.object = self.get_object() + if lesson_date: + self.object = LiveLesson.objects.get(date=datetime.strptime(lesson_date, '%d-%m-%y')) if request.user.is_authenticated: is_purchased = SchoolPayment.objects.filter( user=request.user, diff --git a/project/settings.py b/project/settings.py index 40ac56bb..4cffc20d 100644 --- a/project/settings.py +++ b/project/settings.py @@ -176,6 +176,8 @@ STATICFILES_FINDERS = [ 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 'compressor.finders.CompressorFinder', ] +# FIXME +# STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage' MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media') diff --git a/project/templates/blocks/lil_store_js.html b/project/templates/blocks/lil_store_js.html index 7556240c..cc334c94 100644 --- a/project/templates/blocks/lil_store_js.html +++ b/project/templates/blocks/lil_store_js.html @@ -9,7 +9,7 @@ ADMIN_ROLE: 3, }, pusherKey: '{% setting "PUSHER_KEY" %}', - staticUrl: '{% static "" %}', + staticUrl: '{% get_static_prefix %}', accessToken: '{{ request.user.auth_token }}', isMobile: {{ request.user_agent.is_mobile|yesno:"true,false" }}, defaultUserPhoto: "{% static 'img/user_default.jpg' %}", diff --git a/project/templates/lilcity/index.html b/project/templates/lilcity/index.html index ada31a74..6e1b28a0 100644 --- a/project/templates/lilcity/index.html +++ b/project/templates/lilcity/index.html @@ -155,7 +155,7 @@
{% include 'templates/blocks/lil_store_js.html' %} {% block pre_app_js %}{% endblock pre_app_js %} - +