From 4aad051c723b2806ed0dde9ac7bb550ff0c5d8da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=AD=C3=B0ir=20Valberg=20Gu=C3=B0mundsson?= Date: Fri, 29 Mar 2019 22:06:52 +0100 Subject: [PATCH] More tests - and some blackness. --- src/shop/templatetags/shop_tags.py | 2 +- src/shop/tests.py | 81 ++++++- src/shop/views.py | 375 ++++++++++++++++------------- 3 files changed, 280 insertions(+), 178 deletions(-) diff --git a/src/shop/templatetags/shop_tags.py b/src/shop/templatetags/shop_tags.py index ea4a083b..aaa957b6 100644 --- a/src/shop/templatetags/shop_tags.py +++ b/src/shop/templatetags/shop_tags.py @@ -3,10 +3,10 @@ from decimal import Decimal register = template.Library() + @register.filter def currency(value): try: return "{0:.2f} DKK".format(Decimal(value)) except ValueError: return False - diff --git a/src/shop/tests.py b/src/shop/tests.py index 5e7c1dcb..0fc39228 100644 --- a/src/shop/tests.py +++ b/src/shop/tests.py @@ -231,12 +231,12 @@ class TestOrderDetailView(TestCase): self.client.force_login(self.user) OrderProductRelationFactory(order=self.order) - orp = OrderProductRelationFactory(order=self.order) + opr = OrderProductRelationFactory(order=self.order) - order = orp.order + order = opr.order data = self.base_form_data - data["remove_product"] = orp.pk + data["remove_product"] = opr.pk response = self.client.post(self.path, data=data) self.assertEquals(response.status_code, 200) @@ -248,12 +248,12 @@ class TestOrderDetailView(TestCase): def test_remove_last_product_cancels_order(self): self.client.force_login(self.user) - orp = OrderProductRelationFactory(order=self.order) + opr = OrderProductRelationFactory(order=self.order) - order = orp.order + order = opr.order data = self.base_form_data - data["remove_product"] = orp.pk + data["remove_product"] = opr.pk response = self.client.post(self.path, data=data) self.assertEquals(response.status_code, 302) @@ -266,11 +266,11 @@ class TestOrderDetailView(TestCase): def test_cancel_order(self): self.client.force_login(self.user) - orp = OrderProductRelationFactory(order=self.order) - order = orp.order + opr = OrderProductRelationFactory(order=self.order) + order = opr.order data = self.base_form_data - data["cancel_order"] = None + data["cancel_order"] = "" response = self.client.post(self.path, data=data) self.assertEquals(response.status_code, 302) @@ -280,6 +280,69 @@ class TestOrderDetailView(TestCase): self.assertTrue(order.cancelled) + def test_incrementing_product_quantity(self): + self.client.force_login(self.user) + + opr = OrderProductRelationFactory(order=self.order) + opr.product.stock_amount = 100 + opr.product.save() + + data = self.base_form_data + data["update_order"] = "" + data["form-0-id"] = opr.pk + data["form-0-quantity"] = 11 + + response = self.client.post(self.path, data=data) + opr.refresh_from_db() + self.assertEquals(response.status_code, 200) + self.assertEquals(opr.quantity, 11) + + def test_incrementing_product_quantity_beyond_stock_fails(self): + self.client.force_login(self.user) + + opr = OrderProductRelationFactory(order=self.order) + opr.product.stock_amount = 10 + opr.product.save() + + data = self.base_form_data + data["update_order"] = "" + data["form-0-id"] = opr.pk + data["form-0-quantity"] = 11 + + response = self.client.post(self.path, data=data) + self.assertEquals(response.status_code, 200) + self.assertIn("quantity", response.context["order_product_formset"].errors[0]) + + def test_terms_have_to_be_accepted(self): + self.client.force_login(self.user) + + opr = OrderProductRelationFactory(order=self.order) + + data = self.base_form_data + data["form-0-id"] = opr.pk + data["form-0-quantity"] = 11 + data["payment_method"] = "bank_transfer" + + response = self.client.post(self.path, data=data) + self.assertEquals(response.status_code, 200) + + def test_accepted_terms_and_chosen_payment_method(self): + self.client.force_login(self.user) + + opr = OrderProductRelationFactory(order=self.order) + + data = self.base_form_data + data["form-0-id"] = opr.pk + data["form-0-quantity"] = 11 + data["payment_method"] = "bank_transfer" + data["accept_terms"] = True + + response = self.client.post(self.path, data=data) + self.assertEquals(response.status_code, 302) + self.assertRedirects( + response, reverse("shop:bank_transfer", kwargs={"pk": self.order.id}) + ) + class TestOrderListView(TestCase): def test_order_list_view_as_logged_in(self): diff --git a/src/shop/views.py b/src/shop/views.py index f160dc3a..42d7c721 100644 --- a/src/shop/views.py +++ b/src/shop/views.py @@ -9,19 +9,14 @@ from django.http import ( HttpResponse, HttpResponseRedirect, HttpResponseBadRequest, - Http404 + Http404, ) from django.shortcuts import get_object_or_404 from django.urls import reverse, reverse_lazy from django.utils import timezone from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt -from django.views.generic import ( - View, - ListView, - DetailView, - FormView, -) +from django.views.generic import View, ListView, DetailView, FormView from django.views.generic.base import RedirectView from django.views.generic.detail import SingleObjectMixin @@ -38,7 +33,7 @@ from vendor.coinify.coinify_callback import CoinifyCallback from .coinify import ( create_coinify_invoice, save_coinify_callback, - process_coinify_invoice_json + process_coinify_invoice_json, ) from .epay import calculate_epay_hash, validate_epay_callback from .forms import OrderProductRelationFormSet, OrderProductRelationForm @@ -53,7 +48,7 @@ class EnsureCreditNoteHasPDFMixin(SingleObjectMixin): def dispatch(self, request, *args, **kwargs): if not self.get_object().pdf: messages.error(request, "This creditnote has no PDF yet!") - return HttpResponseRedirect(reverse_lazy('shop:creditnote_list')) + return HttpResponseRedirect(reverse_lazy("shop:creditnote_list")) return super(EnsureCreditNoteHasPDFMixin, self).dispatch( request, *args, **kwargs @@ -83,9 +78,7 @@ class EnsureUserOwnsOrderMixin(SingleObjectMixin): if self.get_object().user != request.user: raise Http404("Order not found") - return super(EnsureUserOwnsOrderMixin, self).dispatch( - request, *args, **kwargs - ) + return super(EnsureUserOwnsOrderMixin, self).dispatch(request, *args, **kwargs) class EnsureUnpaidOrderMixin(SingleObjectMixin): @@ -95,12 +88,10 @@ class EnsureUnpaidOrderMixin(SingleObjectMixin): if self.get_object().paid: messages.error(request, "This order is already paid for!") return HttpResponseRedirect( - reverse_lazy('shop:order_detail', kwargs={'pk': self.get_object().pk}) + reverse_lazy("shop:order_detail", kwargs={"pk": self.get_object().pk}) ) - return super(EnsureUnpaidOrderMixin, self).dispatch( - request, *args, **kwargs - ) + return super(EnsureUnpaidOrderMixin, self).dispatch(request, *args, **kwargs) class EnsurePaidOrderMixin(SingleObjectMixin): @@ -110,12 +101,10 @@ class EnsurePaidOrderMixin(SingleObjectMixin): if not self.get_object().paid: messages.error(request, "This order is not paid for!") return HttpResponseRedirect( - reverse_lazy('shop:order_detail', kwargs={'pk': self.get_object().pk}) + reverse_lazy("shop:order_detail", kwargs={"pk": self.get_object().pk}) ) - return super(EnsurePaidOrderMixin, self).dispatch( - request, *args, **kwargs - ) + return super(EnsurePaidOrderMixin, self).dispatch(request, *args, **kwargs) class EnsureClosedOrderMixin(SingleObjectMixin): @@ -123,14 +112,12 @@ class EnsureClosedOrderMixin(SingleObjectMixin): def dispatch(self, request, *args, **kwargs): if self.get_object().open is not None: - messages.error(request, 'This order is still open!') + messages.error(request, "This order is still open!") return HttpResponseRedirect( - reverse_lazy('shop:order_detail', kwargs={'pk': self.get_object().pk}) + reverse_lazy("shop:order_detail", kwargs={"pk": self.get_object().pk}) ) - return super(EnsureClosedOrderMixin, self).dispatch( - request, *args, **kwargs - ) + return super(EnsureClosedOrderMixin, self).dispatch(request, *args, **kwargs) class EnsureOrderHasProductsMixin(SingleObjectMixin): @@ -138,8 +125,8 @@ class EnsureOrderHasProductsMixin(SingleObjectMixin): def dispatch(self, request, *args, **kwargs): if not self.get_object().products.count() > 0: - messages.error(request, 'This order has no products!') - return HttpResponseRedirect(reverse_lazy('shop:index')) + messages.error(request, "This order has no products!") + return HttpResponseRedirect(reverse_lazy("shop:index")) return super(EnsureOrderHasProductsMixin, self).dispatch( request, *args, **kwargs @@ -152,10 +139,9 @@ class EnsureOrderIsNotCancelledMixin(SingleObjectMixin): def dispatch(self, request, *args, **kwargs): if self.get_object().cancelled: messages.error( - request, - 'Order #{} is cancelled!'.format(self.get_object().id) + request, "Order #{} is cancelled!".format(self.get_object().id) ) - return HttpResponseRedirect(reverse_lazy('shop:index')) + return HttpResponseRedirect(reverse_lazy("shop:index")) return super(EnsureOrderIsNotCancelledMixin, self).dispatch( request, *args, **kwargs @@ -169,7 +155,7 @@ class EnsureOrderHasInvoicePDFMixin(SingleObjectMixin): if not self.get_object().invoice.pdf: messages.error(request, "This order has no invoice yet!") return HttpResponseRedirect( - reverse_lazy('shop:order_detail', kwargs={'pk': self.get_object().pk}) + reverse_lazy("shop:order_detail", kwargs={"pk": self.get_object().pk}) ) return super(EnsureOrderHasInvoicePDFMixin, self).dispatch( @@ -181,17 +167,17 @@ class EnsureOrderHasInvoicePDFMixin(SingleObjectMixin): class ShopIndexView(ListView): model = Product template_name = "shop_index.html" - context_object_name = 'products' + context_object_name = "products" def get_queryset(self): queryset = super(ShopIndexView, self).get_queryset() - return queryset.available().order_by('category__name', 'price', 'name') + return queryset.available().order_by("category__name", "price", "name") def get_context_data(self, **kwargs): context = super(ShopIndexView, self).get_context_data(**kwargs) - if 'category' in self.request.GET: - category = self.request.GET.get('category') + if "category" in self.request.GET: + category = self.request.GET.get("category") # is this a public category try: @@ -202,41 +188,39 @@ class ShopIndexView(ListView): raise Http404("Category not found") # filter products by the chosen category - context['products'] = context['products'].filter( - category__slug=category - ) - context['current_category'] = categoryobj - context['categories'] = ProductCategory.objects.annotate( - num_products=Count('products') + context["products"] = context["products"].filter(category__slug=category) + context["current_category"] = categoryobj + context["categories"] = ProductCategory.objects.annotate( + num_products=Count("products") ).filter( num_products__gt=0, public=True, - products__available_in__contains=timezone.now() + products__available_in__contains=timezone.now(), ) return context class ProductDetailView(FormView, DetailView): model = Product - template_name = 'product_detail.html' + template_name = "product_detail.html" form_class = OrderProductRelationForm - context_object_name = 'product' + context_object_name = "product" def get_form_kwargs(self): kwargs = super().get_form_kwargs() - if hasattr(self, 'opr'): - kwargs['instance'] = self.opr + if hasattr(self, "opr"): + kwargs["instance"] = self.opr return kwargs def get_initial(self): - if hasattr(self, 'opr'): - return {'quantity': self.opr.quantity} + if hasattr(self, "opr"): + return {"quantity": self.opr.quantity} return None def get_context_data(self, **kwargs): # If the OrderProductRelation already exists it has a primary key in the database if self.request.user.is_authenticated and self.opr.pk: - kwargs['already_in_order'] = True + kwargs["already_in_order"] = True return super().get_context_data(**kwargs) @@ -252,32 +236,28 @@ class ProductDetailView(FormView, DetailView): self.opr = OrderProductRelation.objects.get( order__user=self.request.user, order__open__isnull=False, - product=self.object + product=self.object, ) except OrderProductRelation.DoesNotExist: - self.opr = OrderProductRelation( - product=self.get_object(), - quantity=1, - ) + self.opr = OrderProductRelation(product=self.get_object(), quantity=1) - return super(ProductDetailView, self).dispatch( - request, *args, **kwargs - ) + return super(ProductDetailView, self).dispatch(request, *args, **kwargs) def form_valid(self, form): opr = form.save(commit=False) if not opr.pk: - opr.order, _ = Order.objects.get_or_create(user=self.request.user, open=True, cancelled=False) + opr.order, _ = Order.objects.get_or_create( + user=self.request.user, open=True, cancelled=False + ) opr.save() messages.info( self.request, - '{}x {} has been added to your order.'.format( - opr.quantity, - opr.product.name - ) + "{}x {} has been added to your order.".format( + opr.quantity, opr.product.name + ), ) # done @@ -292,22 +272,28 @@ class ProductDetailView(FormView, DetailView): class OrderListView(LoginRequiredMixin, ListView): model = Order template_name = "shop/order_list.html" - context_object_name = 'orders' + context_object_name = "orders" def get_queryset(self): queryset = super(OrderListView, self).get_queryset() return queryset.filter(user=self.request.user).not_cancelled() -class OrderDetailView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureOrderHasProductsMixin, EnsureOrderIsNotCancelledMixin, DetailView): +class OrderDetailView( + LoginRequiredMixin, + EnsureUserOwnsOrderMixin, + EnsureOrderHasProductsMixin, + EnsureOrderIsNotCancelledMixin, + DetailView, +): model = Order - template_name = 'shop/order_detail.html' - context_object_name = 'order' + template_name = "shop/order_detail.html" + context_object_name = "order" def get_context_data(self, **kwargs): - if 'order_product_formset' not in kwargs: - kwargs['order_product_formset'] = OrderProductRelationFormSet( - queryset=OrderProductRelation.objects.filter(order=self.get_object()), + if "order_product_formset" not in kwargs: + kwargs["order_product_formset"] = OrderProductRelationFormSet( + queryset=OrderProductRelation.objects.filter(order=self.get_object()) ) return super().get_context_data(**kwargs) @@ -317,19 +303,19 @@ class OrderDetailView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureOrderH order = self.object # First check if the user is removing a product from the order. - product_remove = request.POST.get('remove_product') + product_remove = request.POST.get("remove_product") if product_remove: order.orderproductrelation_set.filter(pk=product_remove).delete() if not order.products.count() > 0: order.mark_as_cancelled() - messages.info(request, 'Order cancelled!') - return HttpResponseRedirect(reverse_lazy('shop:index')) + messages.info(request, "Order cancelled!") + return HttpResponseRedirect(reverse_lazy("shop:index")) # Then see if the user is cancelling the order. - elif 'cancel_order' in request.POST: + elif "cancel_order" in request.POST: order.mark_as_cancelled() - messages.info(request, 'Order cancelled!') - return HttpResponseRedirect(reverse_lazy('shop:index')) + messages.info(request, "Order cancelled!") + return HttpResponseRedirect(reverse_lazy("shop:index")) # The user is not removing products or cancelling the order, # so from now on we do stuff that require us to check stock. @@ -337,62 +323,58 @@ class OrderDetailView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureOrderH # which product is not in stock if that is the case. else: formset = OrderProductRelationFormSet( - request.POST, - queryset=OrderProductRelation.objects.filter(order=order), + request.POST, queryset=OrderProductRelation.objects.filter(order=order) ) # If the formset is not valid it means that we cannot fulfill the order, so return and inform the user. if not formset.is_valid(): messages.error( request, - "Some of the products you are ordering are out of stock. Review the order and try again." + "Some of the products you are ordering are out of stock. Review the order and try again.", ) return self.render_to_response( self.get_context_data(order_product_formset=formset) ) # No stock issues, proceed to check if the user is updating the order. - if 'update_order' in request.POST: + if "update_order" in request.POST: # We have already made sure the formset is valid, so just save it to update quantities. formset.save() - order.customer_comment = request.POST.get('customer_comment') or '' - order.invoice_address = request.POST.get('invoice_address') or '' + order.customer_comment = request.POST.get("customer_comment") or "" + order.invoice_address = request.POST.get("invoice_address") or "" order.save() # Then at last see if the user is paying for the order. - payment_method = request.POST.get('payment_method') + payment_method = request.POST.get("payment_method") if payment_method in order.PAYMENT_METHODS: - if not request.POST.get('accept_terms'): - messages.error(request, "You need to accept the general terms and conditions before you can continue!") - return HttpResponseRedirect( - reverse_lazy('shop:order_detail', kwargs={'pk': order.pk}) + if not request.POST.get("accept_terms"): + messages.error( + request, + "You need to accept the general terms and conditions before you can continue!", + ) + return self.render_to_response( + self.get_context_data(order_product_formset=formset) ) # Set payment method and mark the order as closed order.payment_method = payment_method order.open = None - order.customer_comment = request.POST.get('customer_comment') or '' - order.invoice_address = request.POST.get('invoice_address') or '' + order.customer_comment = request.POST.get("customer_comment") or "" + order.invoice_address = request.POST.get("invoice_address") or "" order.save() reverses = { Order.CREDIT_CARD: reverse_lazy( - 'shop:epay_form', - kwargs={'pk': order.id} + "shop:epay_form", kwargs={"pk": order.id} ), Order.BLOCKCHAIN: reverse_lazy( - 'shop:coinify_pay', - kwargs={'pk': order.id} + "shop:coinify_pay", kwargs={"pk": order.id} ), Order.BANK_TRANSFER: reverse_lazy( - 'shop:bank_transfer', - kwargs={'pk': order.id} + "shop:bank_transfer", kwargs={"pk": order.id} ), - Order.CASH: reverse_lazy( - 'shop:cash', - kwargs={'pk': order.id} - ) + Order.CASH: reverse_lazy("shop:cash", kwargs={"pk": order.id}), } return HttpResponseRedirect(reverses[payment_method]) @@ -400,12 +382,21 @@ class OrderDetailView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureOrderH return super(OrderDetailView, self).get(request, *args, **kwargs) -class DownloadInvoiceView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsurePaidOrderMixin, EnsureOrderHasInvoicePDFMixin, SingleObjectMixin, View): +class DownloadInvoiceView( + LoginRequiredMixin, + EnsureUserOwnsOrderMixin, + EnsurePaidOrderMixin, + EnsureOrderHasInvoicePDFMixin, + SingleObjectMixin, + View, +): model = Order def get(self, request, *args, **kwargs): - response = HttpResponse(content_type='application/pdf') - response['Content-Disposition'] = 'attachment; filename="%s"' % self.get_object().invoice.filename + response = HttpResponse(content_type="application/pdf") + response["Content-Disposition"] = ( + 'attachment; filename="%s"' % self.get_object().invoice.filename + ) response.write(self.get_object().invoice.pdf.read()) return response @@ -413,19 +404,27 @@ class DownloadInvoiceView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsurePa class CreditNoteListView(LoginRequiredMixin, ListView): model = CreditNote template_name = "shop/creditnote_list.html" - context_object_name = 'creditnotes' + context_object_name = "creditnotes" def get_queryset(self): queryset = super().get_queryset() return queryset.filter(user=self.request.user) -class DownloadCreditNoteView(LoginRequiredMixin, EnsureUserOwnsCreditNoteMixin, EnsureCreditNoteHasPDFMixin, SingleObjectMixin, View): +class DownloadCreditNoteView( + LoginRequiredMixin, + EnsureUserOwnsCreditNoteMixin, + EnsureCreditNoteHasPDFMixin, + SingleObjectMixin, + View, +): model = CreditNote def get(self, request, *args, **kwargs): - response = HttpResponse(content_type='application/pdf') - response['Content-Disposition'] = 'attachment; filename="%s"' % self.get_object().filename + response = HttpResponse(content_type="application/pdf") + response["Content-Disposition"] = ( + 'attachment; filename="%s"' % self.get_object().filename + ) response.write(self.get_object().pdf.read()) return response @@ -436,31 +435,38 @@ class OrderMarkAsPaidView(LoginRequiredMixin, SingleObjectMixin, View): def get(self, request, *args, **kwargs): if not request.user.is_staff: - messages.error(request, 'You do not have permissions to do that.') - return HttpResponseRedirect(reverse_lazy('shop:index')) + messages.error(request, "You do not have permissions to do that.") + return HttpResponseRedirect(reverse_lazy("shop:index")) else: - messages.success(request, 'The order has been marked as paid.') + messages.success(request, "The order has been marked as paid.") order = self.get_object() order.mark_as_paid() - return HttpResponseRedirect(request.META.get('HTTP_REFERER')) + return HttpResponseRedirect(request.META.get("HTTP_REFERER")) # Epay views -class EpayFormView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureUnpaidOrderMixin, EnsureClosedOrderMixin, EnsureOrderHasProductsMixin, DetailView): +class EpayFormView( + LoginRequiredMixin, + EnsureUserOwnsOrderMixin, + EnsureUnpaidOrderMixin, + EnsureClosedOrderMixin, + EnsureOrderHasProductsMixin, + DetailView, +): model = Order - template_name = 'epay_form.html' + template_name = "epay_form.html" def get_context_data(self, **kwargs): order = self.get_object() context = super(EpayFormView, self).get_context_data(**kwargs) - context['merchant_number'] = settings.EPAY_MERCHANT_NUMBER - context['description'] = order.description - context['amount'] = order.total * 100 - context['order_id'] = order.pk - context['accept_url'] = order.get_epay_accept_url(self.request) - context['cancel_url'] = order.get_cancel_url(self.request) - context['callback_url'] = order.get_epay_callback_url(self.request) - context['epay_hash'] = calculate_epay_hash(order, self.request) + context["merchant_number"] = settings.EPAY_MERCHANT_NUMBER + context["description"] = order.description + context["amount"] = order.total * 100 + context["order_id"] = order.pk + context["accept_url"] = order.get_epay_accept_url(self.request) + context["cancel_url"] = order.get_cancel_url(self.request) + context["callback_url"] = order.get_epay_callback_url(self.request) + context["epay_hash"] = calculate_epay_hash(order, self.request) return context @@ -468,21 +474,19 @@ class EpayCallbackView(SingleObjectMixin, View): model = Order def get(self, request, *args, **kwargs): - callback = EpayCallback.objects.create( - payload=request.GET - ) + callback = EpayCallback.objects.create(payload=request.GET) - if 'orderid' in request.GET: + if "orderid" in request.GET: query = OrderedDict( - [tuple(x.split('=')) for x in request.META['QUERY_STRING'].split('&')] + [tuple(x.split("=")) for x in request.META["QUERY_STRING"].split("&")] ) - order = get_object_or_404(Order, pk=query.get('orderid')) + order = get_object_or_404(Order, pk=query.get("orderid")) if order.pk != self.get_object().pk: logger.error("bad epay callback, orders do not match!") return HttpResponse(status=400) if validate_epay_callback(query): - callback.md5valid=True + callback.md5valid = True callback.save() else: logger.error("bad epay callback!") @@ -490,15 +494,13 @@ class EpayCallbackView(SingleObjectMixin, View): if order.paid: # this order is already paid, perhaps we are seeing a double callback? - return HttpResponse('OK') + return HttpResponse("OK") # epay callback is valid - has the order been paid in full? - if int(query['amount']) == order.total * 100: + if int(query["amount"]) == order.total * 100: # create an EpayPayment object linking the callback to the order EpayPayment.objects.create( - order=order, - callback=callback, - txnid=query.get('txnid'), + order=order, callback=callback, txnid=query.get("txnid") ) # and mark order as paid (this will create tickets) order.mark_as_paid(request) @@ -507,53 +509,76 @@ class EpayCallbackView(SingleObjectMixin, View): else: return HttpResponse(status=400) - return HttpResponse('OK') + return HttpResponse("OK") -class EpayThanksView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureClosedOrderMixin, DetailView): +class EpayThanksView( + LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureClosedOrderMixin, DetailView +): model = Order - template_name = 'epay_thanks.html' + template_name = "epay_thanks.html" def dispatch(self, request, *args, **kwargs): if request.GET: # epay redirects the user back to our accepturl with a long # and ugly querystring, redirect user to the clean url return HttpResponseRedirect( - reverse('shop:epay_thanks', kwargs={'pk': self.get_object().pk}) + reverse("shop:epay_thanks", kwargs={"pk": self.get_object().pk}) ) - return super(EpayThanksView, self).dispatch( - request, *args, **kwargs - ) + return super(EpayThanksView, self).dispatch(request, *args, **kwargs) # Bank Transfer view -class BankTransferView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureUnpaidOrderMixin, EnsureOrderHasProductsMixin, DetailView): + +class BankTransferView( + LoginRequiredMixin, + EnsureUserOwnsOrderMixin, + EnsureUnpaidOrderMixin, + EnsureOrderHasProductsMixin, + DetailView, +): model = Order - template_name = 'bank_transfer.html' + template_name = "bank_transfer.html" def get_context_data(self, **kwargs): context = super(BankTransferView, self).get_context_data(**kwargs) - context['iban'] = settings.BANKACCOUNT_IBAN - context['swiftbic'] = settings.BANKACCOUNT_SWIFTBIC - context['orderid'] = self.get_object().pk - context['regno'] = settings.BANKACCOUNT_REG - context['accountno'] = settings.BANKACCOUNT_ACCOUNT - context['total'] = self.get_object().total + context["iban"] = settings.BANKACCOUNT_IBAN + context["swiftbic"] = settings.BANKACCOUNT_SWIFTBIC + context["orderid"] = self.get_object().pk + context["regno"] = settings.BANKACCOUNT_REG + context["accountno"] = settings.BANKACCOUNT_ACCOUNT + context["total"] = self.get_object().total return context # Cash payment view -class CashView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureUnpaidOrderMixin, EnsureOrderHasProductsMixin, DetailView): + +class CashView( + LoginRequiredMixin, + EnsureUserOwnsOrderMixin, + EnsureUnpaidOrderMixin, + EnsureOrderHasProductsMixin, + DetailView, +): model = Order - template_name = 'cash.html' + template_name = "cash.html" # Coinify views -class CoinifyRedirectView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureUnpaidOrderMixin, EnsureClosedOrderMixin, EnsureOrderHasProductsMixin, SingleObjectMixin, RedirectView): + +class CoinifyRedirectView( + LoginRequiredMixin, + EnsureUserOwnsOrderMixin, + EnsureUnpaidOrderMixin, + EnsureClosedOrderMixin, + EnsureOrderHasProductsMixin, + SingleObjectMixin, + RedirectView, +): model = Order def dispatch(self, request, *args, **kwargs): @@ -563,17 +588,20 @@ class CoinifyRedirectView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureUn if not order.coinifyapiinvoice: coinifyinvoice = create_coinify_invoice(order, request) if not coinifyinvoice: - messages.error(request, "There was a problem with the payment provider. Please try again later") + messages.error( + request, + "There was a problem with the payment provider. Please try again later", + ) return HttpResponseRedirect( - reverse_lazy('shop:order_detail', kwargs={'pk': self.get_object().pk}) + reverse_lazy( + "shop:order_detail", kwargs={"pk": self.get_object().pk} + ) ) - return super(CoinifyRedirectView, self).dispatch( - request, *args, **kwargs - ) + return super(CoinifyRedirectView, self).dispatch(request, *args, **kwargs) def get_redirect_url(self, *args, **kwargs): - return self.get_object().coinifyapiinvoice.invoicejson['payment_url'] + return self.get_object().coinifyapiinvoice.invoicejson["payment_url"] class CoinifyCallbackView(SingleObjectMixin, View): @@ -590,34 +618,45 @@ class CoinifyCallbackView(SingleObjectMixin, View): # do we have a json body? if not callbackobject.payload: # no, return an error - logger.error("unable to parse JSON body in callback for order %s" % callbackobject.order.id) - return HttpResponseBadRequest('unable to parse json') + logger.error( + "unable to parse JSON body in callback for order %s" + % callbackobject.order.id + ) + return HttpResponseBadRequest("unable to parse json") # initiate SDK - sdk = CoinifyCallback(settings.COINIFY_IPN_SECRET.encode('utf-8')) + sdk = CoinifyCallback(settings.COINIFY_IPN_SECRET.encode("utf-8")) # attemt to validate the callbackc - if sdk.validate_callback(request.body, request.META['HTTP_X_COINIFY_CALLBACK_SIGNATURE']): + if sdk.validate_callback( + request.body, request.META["HTTP_X_COINIFY_CALLBACK_SIGNATURE"] + ): # mark callback as valid in db callbackobject.valid = True callbackobject.save() else: logger.error("invalid coinify callback detected") - return HttpResponseBadRequest('something is fucky') + return HttpResponseBadRequest("something is fucky") - if callbackobject.payload['event'] == 'invoice_state_change' or callbackobject.payload['event'] == 'invoice_manual_resend': + if ( + callbackobject.payload["event"] == "invoice_state_change" + or callbackobject.payload["event"] == "invoice_manual_resend" + ): process_coinify_invoice_json( - invoicejson=callbackobject.payload['data'], + invoicejson=callbackobject.payload["data"], order=self.get_object(), request=request, ) - return HttpResponse('OK') + return HttpResponse("OK") else: - logger.error("unsupported callback event %s" % callbackobject.payload['event']) - return HttpResponseBadRequest('unsupported event') + logger.error( + "unsupported callback event %s" % callbackobject.payload["event"] + ) + return HttpResponseBadRequest("unsupported event") -class CoinifyThanksView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureClosedOrderMixin, DetailView): +class CoinifyThanksView( + LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureClosedOrderMixin, DetailView +): model = Order - template_name = 'coinify_thanks.html' - + template_name = "coinify_thanks.html"