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.shortcuts import redirect from django.urls import reverse_lazy from django.utils.translation import ugettext_lazy as _ from django.views.generic import ListView 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) 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 form_valid(self, form): if form.is_valid(): try: with transaction.atomic(): form.save() 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() except Exception as e: logger.critical(e) return super().form_invalid(form) 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')