import datetime import logging import uuid from functools import reduce from decimal import Decimal from django.conf import settings from django.db import transaction from django.http import HttpResponseRedirect from django.shortcuts import redirect from django.urls import reverse_lazy from django.utils.translation import ugettext_lazy as _ from django.views.generic import ListView from django.contrib import messages from cart.models import Buying, Discount, Offer from core.models import City from core.views import ProtectedListView, ProtectedTemplateView, ProtectedView, ProtectedFormView, ProtectedBaseFormView from .forms import ( CartRemoveBuyingForm, DiscountForm, CartAddInlineForm, CartCheckoutForm, CartCheckoutBuyingForm) logger = logging.getLogger(__name__) class CartAddView(ProtectedBaseFormView): http_method_names = ('post',) form_class = CartAddInlineForm def get_success_url(self): return self.request.META.get('HTTP_REFERRER', reverse_lazy('products:product_list')) def form_valid(self, form): self.request.cart.add(offer_id=form.cleaned_data['offer'].product_id, quantity=form.cleaned_data['amount']) return super().form_valid(form) def form_invalid(self, form): if form.errors: for error in form.errors: messages.error(self.request, error) return HttpResponseRedirect(self.get_success_url()) class CartRemoveView(ProtectedBaseFormView): http_method_names = ('post',) model = Offer form_class = CartRemoveBuyingForm def get_success_url(self): return self.request.META.get('HTTP_REFERRER', reverse_lazy('products:product_list')) def form_valid(self, form): self.request.cart.remove(form.cleaned_data['offer'].product_id) return super().form_valid(form) class CartView(ProtectedListView): model = Offer paginate_by = settings.DEFAULT_PAGE_AMOUNT context_object_name = 'offer_items' template_name = 'cart/cart.html' ordering = '-create_at' title = _('Корзина') def get_queryset(self): qs = super().get_queryset() return qs.filter(product__id__in=self.request.cart.keys()) def get_total_price(self, object_list): return reduce( lambda initial, offer: initial + offer.price * Decimal(self.request.cart[offer.product_id]['quantity']), object_list, Decimal(0) ) def get_total_cashback(self, object_list): return reduce( lambda initial, offer: initial + offer.cashback * Decimal(self.request.cart[offer.product_id]['quantity']), object_list, Decimal(0) ) def get_context_data(self, *, object_list=None, **kwargs): context = super().get_context_data(object_list=object_list, **kwargs) context['title'] = self.title context['total_price'] = self.get_total_price(self.object_list) context['total_price_currency'] = context[ 'total_cashback_currency'] = self.object_list.first().currency.sign if self.object_list.first() else '' context['total_cashback'] = self.get_total_cashback(self.object_list) return context class CartCheckoutView(ProtectedFormView): http_method_names = ('get', 'post',) template_name = 'cart/checkout.html' form_class = CartCheckoutForm title = _('Оформление заказа') def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['initial'] = { 'customer_name': self.request.user.profile.full_name, 'customer_email': self.request.user.email, 'phone': self.request.user.profile.phone, 'customer_address': self.request.user.profile.address, 'city': City.objects.filter(name=self.request.user.profile.city).first() } return kwargs class CartConfirmView(ProtectedFormView): http_method_names = ('post',) template_name = 'cart/confirm.html' form_class = CartCheckoutForm title = _('Подтверждение заказа') def get_total_price(self, offers): return reduce( lambda initial, offer: initial + offer.price * Decimal(self.request.cart[offer.product_id]['quantity']), offers, Decimal(0) ) def get_form_kwargs(self): kwargs = super().get_form_kwargs() data = kwargs['data'].copy() data.update({ 'total_price': self.get_total_price(Offer.active.filter(product_id__in=self.request.cart.keys())) }) kwargs['data'] = data return kwargs def form_valid(self, form): if form.is_valid(): try: with transaction.atomic(): form.save(self.request.user) for item in self.request.cart: buying_form = CartCheckoutBuyingForm(initial={ 'offer': item, 'order': form.instance.order_code, 'amount': self.request.cart[item]['quantity'] }) buying_form.save(self.request.user) form.send_order_invoice(self.request) form.send_order_request(self.request) self.request.cart.clear() return self.render_to_response(self.get_context_data()) except Exception as e: logger.critical(e) return self.form_invalid(form) def form_invalid(self, form): for error in form.errors: messages.error(self.request, error) return HttpResponseRedirect(self.request.META['HTTP_REFERER']) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['title'] = self.title return context class BuyingsHistory(ProtectedListView): model = Buying paginate_by = settings.DEFAULT_PAGE_AMOUNT context_object_name = 'bought_item_list' ordering = '-create_at' template_name = 'cart/bought_history.html' title = _('История покупок') def get_queryset(self): qs = super().get_queryset() return qs.filter(user=self.request.user) def get_context_data(self, *, object_list=None, **kwargs): context = super().get_context_data(object_list=object_list, **kwargs) context['title'] = self.title return context class DiscountListView(ListView): template_name = 'cart/discount_list.html' model = Discount title = _('Акции / Cashback') def get_context_data(self, *, object_list=None, **kwargs): context = super().get_context_data(object_list=object_list, **kwargs) context['title'] = self.title return context # Discount views # @TODO: TEST FOR PRODUCTION class PointsApply(ProtectedTemplateView): http_method_names = ('post',) def dispatch(self, request, *args, **kwargs): super().dispatch(request, *args, **kwargs) self.request.session['points'] = True return redirect('cart:buyings') class PointsRevoke(ProtectedView): http_method_names = ('post',) def dispatch(self, request, *args, **kwargs): super().dispatch(request, *args, **kwargs) self.request.session.pop('points', None) return redirect('cart:buyings') class DiscountApply(ProtectedFormView): http_method_names = ('post',) form_class = DiscountForm def form_valid(self, form): now = datetime.now() code = form.cleaned_data['code'] try: discount = Discount.objects.get(code__iexact=code, valid_from__lte=now, valid_to__gte=now, active=True) self.request.session['discount_id'] = discount.id except Discount.DoesNotExist: self.request.session['discount_id'] = None return super().form_valid(form) def get_success_url(self): return reverse_lazy('cart:buyings') class CreateDiscount(ProtectedFormView): http_method_names = ('post',) form_class = DiscountForm def form_valid(self, form): now = datetime.now() Discount.objects.update_or_create( user=self.request.user, defaults={'code': str(uuid.uuid4()), 'valid_from': now, 'valid_to': now + datetime.timedelta(days=7), 'active': True} ) return super().form_valid(form) def get_success_url(self): return reverse_lazy('cabinet:index')