import logging import json import decimal from django.shortcuts import redirect, render from django.contrib import messages from django.utils.translation import get_language, ugettext_lazy as _ from django.contrib.auth.decorators import login_required from django.views.decorators.cache import cache_control from django.utils.decorators import method_decorator from django.views import View from django.views.generic import FormView, DetailView from django.views.generic.list import ListView from matrixhosting.forms import InitialRequestForm, RequestHostedVMForm, BillingAddressForm from django.urls import reverse from django.conf import settings from django.http import ( HttpResponseRedirect, JsonResponse, HttpResponse ) from rest_framework import viewsets, permissions from uncloud_pay.models import PricingPlan from uncloud_pay.utils import get_order_total_with_vat from uncloud_pay.models import * from uncloud_pay.utils import validate_vat_number from uncloud_pay.selectors import get_billing_address_for_user, has_enough_balance, get_balance_for_user import uncloud_pay.stripe as uncloud_stripe from .models import VMInstance from .serializers import * from .utils import render_to_pdf logger = logging.getLogger(__name__) class IndexView(FormView): template_name = "matrixhosting/index.html" form_class = InitialRequestForm success_url = "/matrixhosting#requestform" success_message = "Thank you, we will contact you as soon as possible" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['matrix_vm_pricing'] = PricingPlan.get_default_pricing() return context def form_valid(self, form): self.request.session['order'] = form.cleaned_data pricing = get_order_total_with_vat( form.cleaned_data['cores'], form.cleaned_data['memory'], form.cleaned_data['storage'], form.cleaned_data['pricing_name'], False ) self.request.session['pricing'] = pricing return HttpResponseRedirect(reverse('matrix:payment')) class OrderPaymentView(FormView): template_name = 'matrixhosting/order_details.html' success_url = 'matrix:order_confirmation' form_class = BillingAddressForm @method_decorator(login_required) def dispatch(self, *args, **kwargs): return super().dispatch(*args, **kwargs) def get_context_data(self, **kwargs): context = super(OrderPaymentView, self).get_context_data(**kwargs) if 'billing_address_data' in self.request.session: billing_address_form = BillingAddressForm( initial=self.request.session['billing_address_data'] ) else: old_active = get_billing_address_for_user(self.request.user) billing_address_form = BillingAddressForm( instance=old_active ) if old_active else BillingAddressForm( initial={'active': True, 'owner': self.request.user.id} ) details_form = RequestHostedVMForm( initial=self.request.session.get('order', {}) ) balance = get_balance_for_user(self.request.user) customer_id = uncloud_stripe.get_customer_id_for(self.request.user) cards = uncloud_stripe.get_customer_cards(customer_id) context.update({ 'matrix_vm_pricing': PricingPlan.get_by_name(self.request.session.get('pricing', {'name': 'unknown'})['name']), 'billing_address_form': billing_address_form, 'details_form': details_form, 'balance': balance, 'cards': cards, 'stripe_key': settings.STRIPE_PUBLIC_KEY, 'show_cards': True if balance < self.request.session.get('pricing')['total'] else False, }) return context @cache_control(no_cache=True, must_revalidate=True, no_store=True) def get(self, request, *args, **kwargs): for k in ['vat_validation_status', 'token', 'id_payment_method', 'total']: if request.session.get(k): request.session.pop(k) if 'order' not in request.session: return HttpResponseRedirect(reverse('matrix:index')) return self.render_to_response(self.get_context_data()) def post(self, request, *args, **kwargs): details_form = RequestHostedVMForm(request.POST) billing_address_form = BillingAddressForm(request.POST) if not details_form.is_valid() or not billing_address_form.is_valid(): context = self.get_context_data() context.update({'details_form': details_form, 'billing_address_form': billing_address_form}) return self.render_to_response(context) this_user = { 'email': self.request.user.email, 'username': self.request.user.username } address = get_billing_address_for_user(self.request.user) if address: form = BillingAddressForm(self.request.POST, instance=address) else: form = BillingAddressForm(self.request.POST) if form.is_valid: billing_address_ins = form.save() self.request.session["billing_address_id"] = billing_address_ins.id self.request.session['billing_address_data'] = billing_address_form.cleaned_data self.request.session['billing_address_data']['owner'] = self.request.user.id id_payment_method = self.request.POST.get('id_payment_method', False) customer_id = uncloud_stripe.get_customer_id_for(self.request.user) if id_payment_method and id_payment_method != 'undefined': uncloud_stripe.attach_payment_method(id_payment_method, customer_id) vat_number = billing_address_form.cleaned_data.get('vat_number').strip() if vat_number: validate_result = validate_vat_number( stripe_customer_id=customer_id, billing_address_id=billing_address_ins.id ) if 'error' in validate_result and validate_result['error']: messages.add_message( self.request, messages.ERROR, validate_result["error"], extra_tags='vat_error' ) return HttpResponseRedirect( reverse('matrix:payment') + '#vat_error' ) self.request.session["vat_validation_status"] = validate_result["status"] specs = details_form.cleaned_data vat_rate = VATRate.get_vat_rate(billing_address_ins) vat_validation_status = "verified" if billing_address_ins.vat_number_validated_on and billing_address_ins.vat_number_verified else False pricing = get_order_total_with_vat( specs['cores'], specs['memory'], specs['storage'], request.session['pricing']['name'], vat_rate=vat_rate * 100, vat_validation_status = vat_validation_status ) amount = get_balance_for_user(self.request.user) - decimal.Decimal(pricing["total"]) if (amount < 0): payment_id = Payment.deposit(request.user, abs(amount), source='stripe') self.request.session['pricing'] = pricing self.request.session['order'] = specs self.request.session['vat_validation_status'] = vat_validation_status return HttpResponseRedirect(reverse('matrix:order_confirmation')) class OrderDetailsView(DetailView): template_name = "matrixhosting/order_confirmation.html" context_object_name = "order" model = Order @method_decorator(login_required) def dispatch(self, *args, **kwargs): return super().dispatch(*args, **kwargs) @cache_control(no_cache=True, must_revalidate=True, no_store=True) def get(self, request, *args, **kwargs): context = { 'order': self.request.session.get('order'), 'pricing': self.request.session.get('pricing'), 'balance': get_balance_for_user(self.request.user) } if ('order' not in request.session): return HttpResponseRedirect(reverse('matrix:index')) elif 'pricing' not in self.request.session or 'vat_validation_status' not in self.request.session: return HttpResponseRedirect(reverse('matrix:payment')) return render(request, self.template_name, context) def post(self, request, *args, **kwargs): customer = StripeCustomer.objects.get(owner=self.request.user) billing_address = BillingAddress.objects.get(id=request.session.get('billing_address_id')) total = self.request.session['pricing']['total'] order = finalize_order(request, customer, billing_address, total, PricingPlan.get_by_name(self.request.session['pricing']['name']), request.session.get('order')) if order: bill = Bill.create_next_bill_for_user_address(billing_address) payment= Payment.withdraw(owner=request.user, amount=total, notes=f"BILL #{bill.id}") if payment: #Close the bill as the payment has been added bill.close() response = { 'status': True, 'redirect': (reverse('matrix:dashboard')), 'msg_title': str(_('Thank you for the order.')), 'msg_body': str( _('Your VM will be up and running in a few moments.' ' We will send you a confirmation email as soon as' ' it is ready.')) } return JsonResponse(response) def finalize_order(request, customer, billing_address, one_time_price, pricing_plan, specs): product = Product.objects.first() recurring_period_product = ProductToRecurringPeriod.objects.filter(product=product, is_default=True).first() order = Order.objects.create( owner=request.user, customer=customer, billing_address=billing_address, one_time_price=one_time_price, pricing_plan=pricing_plan, recurring_period= recurring_period_product.recurring_period, product = product, config=json.dumps(specs) ) return order class OrderSuccessView(DetailView): template_name = "matrixhosting/order_success.html" context_object_name = "order" model = Order @method_decorator(login_required) def dispatch(self, *args, **kwargs): return super().dispatch(*args, **kwargs) @cache_control(no_cache=True, must_revalidate=True, no_store=True) def get(self, request, *args, **kwargs): context = { 'order': self.request.session.get('order'), 'balance': get_balance_for_user(self.request.user) } # if ('order' not in request.session): # return HttpResponseRedirect(reverse('matrix:index')) return render(request, self.template_name, context) class InvoiceDownloadView(View): def get(self, request, *args, **kwargs): data = { 'today': datetime.date.today(), 'amount': 39.99, 'customer_name': 'Cooper Mann', 'order_id': 1233434, } pdf = render_to_pdf('matrixhosting/invoice.html', data) if pdf: response = HttpResponse(pdf, content_type='application/pdf') content = "inline; filename=invoice.pdf" content = "attachment; filename=invoice.pdf" response['Content-Disposition'] = content return response return HttpResponse("Not found") class Dashboard(ListView): template_name = "matrixhosting/dashboard.html" model = Order @method_decorator(login_required) def dispatch(self, *args, **kwargs): return super().dispatch(*args, **kwargs) def get_queryset(self): return Order.objects.filter(owner=self.request.user) def post(self, request, *args, **kwargs): order = Order.objects.get(id=request.POST.get('order_id', 0)) order.cancel() return JsonResponse({'message': 'Successfully Cancelled'}) class MachineViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = VMInstanceSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): return VMInstance.objects.filter(owner=self.request.user) class PaymentsView(ListView): template_name = "matrixhosting/payments.html" model = Payment @method_decorator(login_required) def dispatch(self, *args, **kwargs): return super().dispatch(*args, **kwargs) def get_context_data(self, **kwargs): context = super(PaymentsView, self).get_context_data(**kwargs) context.update({ 'balance': get_balance_for_user(self.request.user) }) return context def get_queryset(self): return Payment.objects.filter(owner=self.request.user)