Test Full cycle and fix some issues

This commit is contained in:
amalelshihaby 2021-08-16 10:46:59 +02:00
parent 1aeb757e22
commit 2e05ebbf67
19 changed files with 131 additions and 98 deletions

View file

@ -11,7 +11,7 @@ from django.conf import settings
from django.contrib.auth import get_user_model
from django.template.loader import render_to_string
from uncloud_pay.models import Order
from uncloud_pay.models import Order, BillRecord
# Initialize logger.
@ -71,6 +71,24 @@ class VMInstance(models.Model):
def __str__(self):
return f"{self.id}-{self.order}"
def delete_for_bill(self, bill):
#TODO delete related instances
@classmethod
def delete_for_bill(cls, bill):
bill_records = BillRecord.objects.filter(bill=bill)
for record in bill_records:
instances = VMInstance.objects.filter(order=record.order)
for instance in instances:
instance.delete()
return True
@classmethod
def create_instance(cls, order):
machine = cls.objects.filter(order=order).first()
if not machine:
order_config = json.loads(order.config)
isOpenRegistration = order_config.get('is_open_registration', False)
instance_config = {'cpuCores': order_config['cores'], 'ram': order_config['memory'], 'storage': order_config['storage'],
'matrixDomain': order_config['matrix_domain'], 'homeserverDomain': order_config['homeserver_domain'],
'webClientDomain': order_config['webclient_domain'], 'isOpenRegistration': isOpenRegistration}
cls.objects.create(owner=order.owner, order=order, vm_name=order_config['homeserver_domain'],
homeserver_domain=order_config['homeserver_domain'],webclient_domain=order_config['webclient_domain'],
config=instance_config)

View file

@ -1,19 +1,2 @@
import json
from matrixhosting.models import VMInstance
from uncloud_pay.models import Order
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=Order)
def create_instance(sender, instance, created, **kwargs):
if not created:
return
machine = VMInstance.objects.filter(order=instance).first()
if not machine:
order_config = json.loads(instance.config)
instance_config = {'cpuCores': order_config['cores'], 'ram': order_config['memory'], 'storage': order_config['storage'],
'matrixDomain': order_config['matrix_domain'], 'homeserverDomain': order_config['homeserver_domain'],
'webClientDomain': order_config['webclient_domain'], 'isOpenRegistration': order_config['is_open_registration']}
VMInstance.objects.create(owner=instance.owner, order=instance, vm_name=order_config['homeserver_domain'],
homeserver_domain=order_config['homeserver_domain'],webclient_domain=order_config['webclient_domain'],
config=instance_config)

View file

@ -44,7 +44,35 @@
<div id="main-wrapper">
{% block navbar %} {% include "matrixhosting/includes/_navbar.html" with transparent_header=transparent_header %} {%endblock %}
{% block main %}
<div class="container">
<!-- Steps Progress bar -->
{% if messages %}
<div class="row">
{% for message in messages %}
{% if 'error' in message.tags %}
<div class="col-lg-12 alert alert-danger" role="alert">
<h4 class="alert-heading">{%trans "Error!" %}</h4>
<p class="mb-0">
{{ message|safe }}
</p>
</div>
{% else %}
<div class="col-lg-12 alert alert-success" role="alert">
<p class="mb-0 float-left">
{{ message|safe }}
</p>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% block content %} {% endblock %}
</div>
{% endblock %}
{% include "matrixhosting/includes/_footer.html" %}
</div>

View file

@ -5,7 +5,6 @@
{% block title %} Bills {% endblock %}
{% block content %}
<div class="container">
<div class="bg-white shadow-md rounded p-4 my-4">
<div class="row">
<div class="col-md-3">
@ -182,7 +181,6 @@
</div>
</div>
</div>
</div>
{% endblock %}
{% block js_extra %}
<script>

View file

@ -5,7 +5,6 @@
{% block title %} Payment Methods {% endblock %}
{% block content %}
<div class="container">
<div class="bg-white shadow-md rounded p-4 my-4">
<div class="row">
<div class="col-md-3">
@ -128,7 +127,6 @@
</div>
</div>
</div>
</div>
{% endblock %}
{% block js_extra %}
<script src="https://js.stripe.com/v3/"></script>

View file

@ -18,6 +18,7 @@
============================== -->
{% url 'matrix:index' as index_url %}
{% url 'matrix:orders' as orders_url %}
{% url 'matrix:instances' as instances_url %}
{% url 'matrix:billing' as payments_url %}
<nav class="primary-menu navbar navbar-expand-lg">
<div id="header-nav" class="collapse navbar-collapse">
@ -27,8 +28,9 @@
<li><a href="">Pricing</a></li>
<li><a href="">Contact Us</a></li>
{% else %}
<li><a class="{% if request.path == orders_url %}active{%endif%}" href="{{orders_url}}">Orders</a></li>
<li class="{% if request.path == payments_url %}active{%endif%}"><a href="{{payments_url}}">{%trans "Billing" %}</a></li>
<li class="{% if orders_url in request.path %}active{%endif%}"><a href="{{orders_url}}">Orders</a></li>
<li class="{% if instances_url in request.path %}active{%endif%}"><a href="{{instances_url}}">Instances</a></li>
<li class="{% if payments_url in request.path %}active{%endif%}"><a href="{{payments_url}}">{%trans "Billing" %}</a></li>
<li><a href="">Help</a></li>
{% endif %}
</ul>

View file

@ -16,7 +16,7 @@
{% endwith %}
{% endblock %}
{% block content %}
{% block main %}
<!-- Content
============================================= -->
<div id="content">

View file

@ -7,18 +7,26 @@
{% block content %}
<!-- Page Content -->
{% csrf_token %}
<div class="container">
<div class="row p-1 mt-4">
<div class="col-lg-12 bg-white shadow-sm border border-light rounded py-4 mb-4">
<div class="row">
<div class="col-6 col-sm-6">
<h3 class="text-5 font-weight-400 d-flex align-items-center px-1 mb-4">{% trans "Instances"%}</h3>
</div>
<div class="col-6 col-sm-6">
<div class="float-right">
<a class="border border-primary rounded p-2" href="{% url 'matrix:payment' %}">{% trans "Request New Instance"%}</a>
</div>
</div>
</div>
<!-- Title
=============================== -->
<div class="transaction-title py-2 px-1">
<div class="row">
<div class="col-1 col-sm-1 text-center"><span class="">{% trans "ID"%}</span></div>
<div class="col-2 col-sm-2 text-center"><span class="">{% trans "Creation Date"%}</span></div>
<div class="col-3 col-sm-3 d-none d-sm-block text-center">{% trans "Homeserver Domain"%}</div>
<div class="col-3 col-sm-3 text-right">{% trans "WebClient Domain"%}</div>
<div class="col-3 col-sm-3 text-center">{% trans "Homeserver Domain"%}</div>
<div class="col-3 col-sm-3 text-center">{% trans "WebClient Domain"%}</div>
<div class="col-1 col-sm-1 text-center"><span class="">{% trans "Order"%}</span></div>
<div class="col-2 col-sm-2 text-center">{% trans "Termination Date"%}</div>
</div>
@ -33,10 +41,10 @@
<div class="row align-items-center flex-row">
<div class="col-1 col-sm-1 text-center"> <span class="d-block text-3 font-weight-400">#{{instance.id}}</span></div>
<div class="col-2 col-sm-2 text-center"> <span class="d-block text-2 font-weight-300">{{instance.creation_date|date:"Y-m-d"}}</span></div>
<div class="col-3 col-sm-3 d-none d-sm-block text-right text-1"> <span>{{instance.homeserver_domain}}</span> </div>
<div class="col-3 col-sm-3 d-none d-sm-block text-right text-1"> <span>{{instance.webclient_domain}}</span> </div>
<div class="col-3 col-sm-3 text-center text-1"> <span>{{instance.homeserver_domain}}</span> </div>
<div class="col-3 col-sm-3 text-center text-1"> <span>{{instance.webclient_domain}}</span> </div>
<div class="col-1 col-sm-1 text-center text-2"><span class="">#{{instance.order.id}}</span></div>
<div class="col-2 col-sm-2 d-none d-sm-block text-center text-2"> <span class=" text-uppercase">{{order.termination_date|date:"Y-m-d"}}</span> </div>
<div class="col-2 col-sm-2 text-center text-2"> <span class=" text-uppercase">{{order.termination_date|date:"Y-m-d"}}</span> </div>
</div>
</div>
{%endfor%}
@ -45,8 +53,6 @@
</div>
</div>
</div>
{% endblock %}
{% block js_extra %}

View file

@ -5,7 +5,6 @@
{% block title %} Request Details {% endblock %}
{% block content %}
<div class="container">
<!-- Steps Progress bar -->
<div class="row mt-3 mb-4">
<div class="col-lg-12 mx-auto mb-4">
@ -15,7 +14,7 @@
<div class="progress">
<div class="progress-bar"></div>
</div>
<a href="#" class="step-dot"></a> </div>
<a href="{% url 'matrix:payment' %}" class="step-dot"></a> </div>
<div class="col-4 step active">
<div class="step-name">{%trans "Confirm" %}</div>
<div class="progress">
@ -199,7 +198,6 @@
</div>
</div>
</div>
</div>
{% endblock %}
{% block js_extra %}

View file

@ -5,31 +5,6 @@
{% block title %} Request Details {% endblock %}
{% block content %}
<div class="container">
<!-- Steps Progress bar -->
{% if messages %}
<div class="row">
{% for message in messages %}
{% if 'error' in message.tags %}
<div class="col-lg-12 alert alert-danger" role="alert">
<h4 class="alert-heading">{%trans "Error!" %}</h4>
<p class="mb-0">
{{ message|safe }}
</p>
</div>
{% else %}
<div class="col-lg-12 alert alert-success" role="alert">
<p class="mb-0 float-left">
{{ message|safe }}
</p>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
<div class="row mt-3 mb-4">
<div class="col-lg-12 mx-auto mb-4">
<div class="row widget-steps">
@ -264,8 +239,6 @@
</div>
</div>
</div>
</div>
{% endblock %}
{% block js_extra %}

View file

@ -5,7 +5,6 @@
{% block title %} Request Details {% endblock %}
{% block content %}
<div class="container">
<div class="row mt-3 mb-4">
<div class="col-lg-12 mx-auto mb-4">
<div class="row widget-steps">
@ -41,9 +40,8 @@
<p class="text-success text-8 font-weight-500 line-height-07">{%trans "Success!" %}</p>
<p class="lead">{%trans "Order has been successfully added. Your VM will be up and running in a few moments. " %}</p>
</div>
<p class="text-3 mb-4">{%trans "We will send you a confirmation email as soon as it is ready." %} {%trans "Go to your dashboard" %} <a class="btn-link" href="{% url 'matrix:billing' %}">{%trans "Billing" %}</a>.</p>
<a href="{% url 'matrix:invoice_download' %}" class="btn btn-white border border-success text-success" role="button"><i class="fas fa-print"></i> {%trans "Download Invoice" %}</a>
</div>
<p class="text-3 mb-4">{%trans "We will send you a confirmation email as soon as it is ready." %} {%trans "Go to your dashboard" %} <a class="btn-link" href="{% url 'matrixhosting:bills' %}">{%trans "Billing" %}</a>.</p>
<a href="{% url 'matrix:invoice_download' bill_id=bill_id %}" class="btn btn-white border border-success text-success" role="button"><i class="fas fa-print"></i> {%trans "Download Invoice" %}</a>
</div>
</div>
</div>

View file

@ -7,7 +7,6 @@
{% block content %}
<!-- Page Content -->
{% csrf_token %}
<div class="container">
<div class="row p-1 mt-4">
<div class="col-lg-12 bg-white shadow-sm border border-light rounded py-4 mb-4">
<h3 class="text-5 font-weight-400 d-flex align-items-center px-1 mb-4">{% trans "Orders"%}</h3>
@ -104,8 +103,6 @@
</div>
</div>
</div>
</div>
<div class="alert" role="alert" id="result"></div>
<!-- /.banner -->
{% endblock %}

View file

@ -5,7 +5,6 @@
{% block title %} Payments {% endblock %}
{% block content %}
<div class="container">
<div class="bg-white shadow-md rounded p-4 my-4">
<div class="row">
<div class="col-md-3">
@ -116,7 +115,6 @@
</div>
</div>
</div>
</div>
{% endblock %}
{% block js_extra %}

View file

@ -6,7 +6,7 @@ from django.contrib.auth import get_user_model
from django.utils import timezone
from .models import VMInstance
from uncloud_pay.models import Order, PricingPlan, BillingAddress, Product, RecurringPeriod
from uncloud_pay.models import Order, PricingPlan, BillingAddress, Product, RecurringPeriod, Bill, BillRecord
vm_product_config = {
@ -54,6 +54,26 @@ class VMInstanceTestCase(TestCase):
self.product.recurring_periods.add(self.default_recurring_period,
through_defaults= { 'is_default': True })
def test_delete_matrix_vm_related_to_bill(self):
order = Order.objects.create(owner=self.user,
recurring_period=self.default_recurring_period,
billing_address=self.ba,
pricing_plan = self.pricing_plan,
product=self.product,
config=self.config)
VMInstance.create_instance(order)
instances = VMInstance.objects.filter(order=order)
self.assertEqual(len(instances), 1)
bill = Bill.create_next_bill_for_order(order, ending_date=order.next_cancel_or_downgrade_date(order.starting_date))
bill_records = BillRecord.objects.filter(bill=bill)
self.assertEqual(len(bill_records), 1)
self.assertEqual(bill_records[0].order, order)
VMInstance.delete_for_bill(bill)
instances = VMInstance.objects.filter(order=order)
self.assertEqual(len(instances), 0)
def test_create_matrix_vm(self):
order = Order.objects.create(owner=self.user,
recurring_period=self.default_recurring_period,
@ -61,6 +81,7 @@ class VMInstanceTestCase(TestCase):
pricing_plan = self.pricing_plan,
product=self.product,
config=self.config)
VMInstance.create_instance(order)
instances = VMInstance.objects.filter(order=order)
self.assertEqual(len(instances), 1)

View file

@ -1,5 +1,6 @@
import json
from uncloud_pay.models import Product, Order, ProductToRecurringPeriod
from uncloud_pay.models import Product, Order, ProductToRecurringPeriod, Bill, Payment
from .models import VMInstance
def finalize_order(request, customer, billing_address,
@ -17,4 +18,12 @@ def finalize_order(request, customer, billing_address,
product = product,
config=json.dumps(specs)
)
return order
if order:
bill = Bill.create_next_bill_for_order(order, ending_date=order.next_cancel_or_downgrade_date(order.starting_date))
payment= Payment.withdraw(owner=request.user, amount=one_time_price, notes=f"BILL #{bill.id}")
if payment:
#Close the bill as the payment has been added
VMInstance.create_instance(order)
bill.close()
return order, bill

View file

@ -68,6 +68,7 @@ class OrderPaymentView(FormView):
def get_context_data(self, **kwargs):
context = super(OrderPaymentView, self).get_context_data(**kwargs)
default_pricing = PricingPlan.get_default_pricing()
if 'billing_address_data' in self.request.session:
billing_address_form = BillingAddressForm(
initial=self.request.session['billing_address_data']
@ -80,21 +81,22 @@ class OrderPaymentView(FormView):
initial={'active': True, 'owner': self.request.user.id}
)
details_form = InitialRequestForm(
initial=self.request.session.get('order', {})
initial=self.request.session.get('order', {'pricing_name': default_pricing.name})
)
balance = get_balance_for_user(self.request.user)
customer_id = uncloud_stripe.get_customer_id_for(self.request.user)
#TODO optimize this part for better performance
uncloud_stripe.sync_cards_for_user(self.request.user)
cards = uncloud_stripe.get_customer_cards(customer_id)
pricing = self.request.session.get('pricing', {'name': default_pricing.name, 'total': 0})
context.update({
'matrix_vm_pricing': PricingPlan.get_by_name(self.request.session.get('pricing', {'name': 'unknown'})['name']),
'matrix_vm_pricing': PricingPlan.get_by_name(pricing['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,
'show_cards': True if balance < pricing['total'] else False,
})
return context
@ -103,8 +105,6 @@ class OrderPaymentView(FormView):
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):
@ -213,19 +213,23 @@ class OrderConfirmationView(DetailView):
try:
amount = get_balance_for_user(self.request.user) - decimal.Decimal(total)
if (amount < 0):
Payment.deposit(request.user, abs(max(amount, settings.MIN_PER_TRANSACTION)), source='stripe')
order = finalize_order(request, customer,
Payment.deposit(request.user, max(abs(amount), settings.MIN_PER_TRANSACTION), source='stripe')
amount = get_balance_for_user(self.request.user) - decimal.Decimal(total)
if (amount < 0):
messages.add_message(
self.request, messages.ERROR, "Please make sure that you have enough balance in your wallet and try again later.",
extra_tags='error'
)
return HttpResponseRedirect(
reverse('matrix:order_confirmation')
)
order, bill = 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_order(order)
if order and bill:
self.request.session['bill_id'] = bill.id
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()
return HttpResponseRedirect(reverse('matrix:order_success'))
except CardError as e:
messages.add_message(
@ -252,6 +256,7 @@ class OrderSuccessView(DetailView):
def get(self, request, *args, **kwargs):
context = {
'order': self.request.session.get('order'),
'bill_id': self.request.session['bill_id'],
'balance': get_balance_for_user(self.request.user)
}
if ('order' not in request.session):
@ -294,7 +299,7 @@ class OrdersView(ListView):
return super().dispatch(*args, **kwargs)
def get_queryset(self):
return Order.objects.filter(owner=self.request.user)
return Order.objects.filter(owner=self.request.user).order_by('-creation_date')
def post(self, request, *args, **kwargs):
order = Order.objects.get(id=request.POST.get('order_id', 0))
@ -310,7 +315,7 @@ class InstancesView(ListView):
return super().dispatch(*args, **kwargs)
def get_queryset(self):
return VMInstance.objects.filter(owner=self.request.user)
return VMInstance.objects.filter(owner=self.request.user).order_by('-creation_date')
class PaymentsView(ListView):
template_name = "matrixhosting/payments.html"

View file

@ -16,4 +16,4 @@ GITLAB_PROJECT_ID=
GITLAB_OAUTH_TOKEN=
GITLAB_AUTHOR_EMAIL=
GITLAB_AUTHOR_NAME=
WKHTMLTOPDF_CMD=/usr/local/bin/wkhtmltopdf
WKHTMLTOPDF_CMD=

View file

@ -856,7 +856,6 @@ class Order(models.Model):
def create_bill_record(self, bill):
br = None
if self.recurring_price != 0:
records = BillRecord.objects.filter(order=self).all()
if not records:

View file

@ -38,9 +38,11 @@ logger = logging.getLogger(__name__)
class PricingView(View):
def get(self, request, **args):
address = get_billing_address_for_user(self.request.user)
vat_rate = False
vat_validation_status = False
address = False
if self.request.user and self.request.user.is_authenticated:
address = get_billing_address_for_user(self.request.user)
if address:
vat_rate = VATRate.get_vat_rate(address)
vat_validation_status = "verified" if address.vat_number_validated_on and address.vat_number_verified else False