Add hosting bill view, model and urls
This commit is contained in:
parent
9884eefa19
commit
2ff8b9e4a5
9 changed files with 330 additions and 3 deletions
|
@ -5,7 +5,8 @@ from django.core.urlresolvers import reverse
|
||||||
from utils.mailer import BaseEmail
|
from utils.mailer import BaseEmail
|
||||||
|
|
||||||
from .forms import HostingOrderAdminForm
|
from .forms import HostingOrderAdminForm
|
||||||
from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder, ManageVM
|
from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder, \
|
||||||
|
ManageVM, HostingBill
|
||||||
from .opennebula_functions import HostingManageVMAdmin
|
from .opennebula_functions import HostingManageVMAdmin
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,3 +99,4 @@ admin.site.register(HostingOrder, HostingOrderAdmin)
|
||||||
admin.site.register(VirtualMachineType)
|
admin.site.register(VirtualMachineType)
|
||||||
admin.site.register(VirtualMachinePlan, VirtualMachinePlanAdmin)
|
admin.site.register(VirtualMachinePlan, VirtualMachinePlanAdmin)
|
||||||
admin.site.register(ManageVM, HostingManageVMAdmin)
|
admin.site.register(ManageVM, HostingManageVMAdmin)
|
||||||
|
admin.site.register(HostingBill)
|
||||||
|
|
24
hosting/migrations/0028_managevms.py
Normal file
24
hosting/migrations/0028_managevms.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2017-04-24 04:24
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('hosting', '0027_auto_20160711_0210'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ManageVMs',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'managed': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
24
hosting/migrations/0029_managevm.py
Normal file
24
hosting/migrations/0029_managevm.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2017-04-24 04:25
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('hosting', '0028_managevms'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ManageVM',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'managed': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
31
hosting/migrations/0030_hostingbill.py
Normal file
31
hosting/migrations/0030_hostingbill.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2017-05-05 11:50
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import utils.mixins
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('utils', '0005_auto_20170322_1443'),
|
||||||
|
('membership', '0006_auto_20160526_0445'),
|
||||||
|
('hosting', '0029_managevm'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='HostingBill',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('billing_address', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='utils.BillingAddress')),
|
||||||
|
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='membership.StripeCustomer')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'permissions': (('view_hostingbill', 'View Hosting Bill'),),
|
||||||
|
},
|
||||||
|
bases=(utils.mixins.AssignPermissionsMixin, models.Model),
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,5 +1,6 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
import oca
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
@ -233,3 +234,18 @@ class ManageVM(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
managed = False
|
managed = False
|
||||||
|
|
||||||
|
class HostingBill(AssignPermissionsMixin, models.Model):
|
||||||
|
customer = models.ForeignKey(StripeCustomer)
|
||||||
|
billing_address = models.ForeignKey(BillingAddress)
|
||||||
|
|
||||||
|
permissions = ('view_hostingbill',)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
permissions = (
|
||||||
|
('view_hostingbill', 'View Hosting Bill'),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "%s" % (self.customer.user.email)
|
||||||
|
|
||||||
|
|
93
hosting/templates/hosting/bill_detail.html
Normal file
93
hosting/templates/hosting/bill_detail.html
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
{% extends "hosting/base_short.html" %}
|
||||||
|
{% load staticfiles bootstrap3 %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h1> {{ bill }} </h1>
|
||||||
|
<div class="container">
|
||||||
|
{# Adress bar #}
|
||||||
|
<div class="row">
|
||||||
|
<div class="invoice-title">
|
||||||
|
<h2>{% trans "Invoice"%}</h2><h3 class="pull-right">{% trans "Order #"%} {{bill.id}}</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<address>
|
||||||
|
{{bill.customer.user.name}}<br>
|
||||||
|
{{bill.billing_address.street_address}},{{bill.billing_address.postal_code}}<br>
|
||||||
|
{{bill.billing_address.city}}, {{bill.billing_address.country}}.
|
||||||
|
</address>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6 text-right">
|
||||||
|
<address>
|
||||||
|
{% trans "ungleich GmbH" %}<br>
|
||||||
|
{% trans "buchhaltung@ungleich.ch" %}<br>
|
||||||
|
{% trans "Hauptstrasse 14"%}<br>
|
||||||
|
{% trans "CH-8775 Luchsingen"%}<br>
|
||||||
|
{% trans "Mwst-Nummer: CHE-109.549.333 MWST"%}<br>
|
||||||
|
|
||||||
|
</address>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<table class="table table-bordered">
|
||||||
|
{# Bill header #}
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Cores</th>
|
||||||
|
<th>Memory</th>
|
||||||
|
<th>Disk Size</th>
|
||||||
|
<th>Price</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{# Bill items#}
|
||||||
|
{% for vm in vms %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ vm.name }}</td>
|
||||||
|
<td>{{ vm.cores }}</td>
|
||||||
|
<td>{{ vm.memory }}</td>
|
||||||
|
<td>{{ vm.disk_size }}</td>
|
||||||
|
<td>{{ vm.price }}</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{# Bill total#}
|
||||||
|
<tr>
|
||||||
|
<td> {% trans "Total:" %} </td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td> {% trans "Brutto" %} </td>
|
||||||
|
<td> {% trans "Netto" %} </td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<hr>
|
||||||
|
{# Bill Footer #}
|
||||||
|
<div class="row">
|
||||||
|
{% trans "Alles Preise in CHF mit 8% Mehrwertsteuer." %}
|
||||||
|
{% trans "Betrag zahlbar innerhalb von 30 Tagen ab Rechnungseingang." %}
|
||||||
|
{% trans "Kontoverbindung:" %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
{% trans "IBAN:" %}
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
{% trans "BIC:" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
{% trans "CH02 ............" %}
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
{% trans "POFICHBEXXX" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
60
hosting/templates/hosting/bills.html
Normal file
60
hosting/templates/hosting/bills.html
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
{% extends "hosting/base_short.html" %}
|
||||||
|
{% load staticfiles bootstrap3 %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="container orders-container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8 col-md-offset-2">
|
||||||
|
<table class="table borderless table-hover">
|
||||||
|
<h3>{% trans "Customers"%}</h3>
|
||||||
|
<br/>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Name"%}</th>
|
||||||
|
<th>{% trans "Email"%}</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for user in users %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ user.name}}</td>
|
||||||
|
<td>{{ user.email}} CHF</td>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="btn btn-default"><a
|
||||||
|
href="{% url 'hosting:bills' user.id %}">{% trans "View Bill"%}</a>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% if is_paginated %}
|
||||||
|
<div class="pagination">
|
||||||
|
<span class="page-links">
|
||||||
|
{% if page_obj.has_previous %}
|
||||||
|
<a href="{{ request.path }}?page={{ page_obj.previous_page_number }}">{% trans "previous"%}</a>
|
||||||
|
{% endif %}
|
||||||
|
<span class="page-current">
|
||||||
|
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
|
||||||
|
</span>
|
||||||
|
{% if page_obj.has_next %}
|
||||||
|
<a href="{{ request.path }}?page={{ page_obj.next_page_number }}">{% trans "next"%}</a>
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -4,7 +4,8 @@ from .views import DjangoHostingView, RailsHostingView, PaymentVMView,\
|
||||||
NodeJSHostingView, LoginView, SignupView, IndexView, \
|
NodeJSHostingView, LoginView, SignupView, IndexView, \
|
||||||
OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView,\
|
OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView,\
|
||||||
VirtualMachineView, GenerateVMSSHKeysView, OrdersHostingDeleteView, NotificationsView, \
|
VirtualMachineView, GenerateVMSSHKeysView, OrdersHostingDeleteView, NotificationsView, \
|
||||||
MarkAsReadNotificationView, PasswordResetView, PasswordResetConfirmView, HostingPricingView
|
MarkAsReadNotificationView, PasswordResetView, PasswordResetConfirmView, \
|
||||||
|
HostingPricingView, HostingBillListView, HostingBillDetailView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'index/?$', IndexView.as_view(), name='index'),
|
url(r'index/?$', IndexView.as_view(), name='index'),
|
||||||
|
@ -15,6 +16,8 @@ urlpatterns = [
|
||||||
url(r'payment/?$', PaymentVMView.as_view(), name='payment'),
|
url(r'payment/?$', PaymentVMView.as_view(), name='payment'),
|
||||||
url(r'orders/?$', OrdersHostingListView.as_view(), name='orders'),
|
url(r'orders/?$', OrdersHostingListView.as_view(), name='orders'),
|
||||||
url(r'orders/(?P<pk>\d+)/?$', OrdersHostingDetailView.as_view(), name='orders'),
|
url(r'orders/(?P<pk>\d+)/?$', OrdersHostingDetailView.as_view(), name='orders'),
|
||||||
|
url(r'bills/?$', HostingBillListView.as_view(), name='bills'),
|
||||||
|
url(r'bills/(?P<pk>\d+)/?$', HostingBillDetailView.as_view(), name='bills'),
|
||||||
url(r'cancel_order/(?P<pk>\d+)/?$', OrdersHostingDeleteView.as_view(), name='delete_order'),
|
url(r'cancel_order/(?P<pk>\d+)/?$', OrdersHostingDeleteView.as_view(), name='delete_order'),
|
||||||
url(r'my-virtual-machines/?$', VirtualMachinesPlanListView.as_view(), name='virtual_machines'),
|
url(r'my-virtual-machines/?$', VirtualMachinesPlanListView.as_view(), name='virtual_machines'),
|
||||||
url(r'my-virtual-machines/(?P<pk>\d+)/?$', VirtualMachineView.as_view(),
|
url(r'my-virtual-machines/(?P<pk>\d+)/?$', VirtualMachineView.as_view(),
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import oca
|
||||||
|
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.core.urlresolvers import reverse_lazy, reverse
|
from django.core.urlresolvers import reverse_lazy, reverse
|
||||||
|
@ -19,7 +20,7 @@ from utils.stripe_utils import StripeUtils
|
||||||
from utils.forms import BillingAddressForm, PasswordResetRequestForm
|
from utils.forms import BillingAddressForm, PasswordResetRequestForm
|
||||||
from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin
|
from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin
|
||||||
from utils.mailer import BaseEmail
|
from utils.mailer import BaseEmail
|
||||||
from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder
|
from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder, HostingBill
|
||||||
from .forms import HostingUserSignupForm, HostingUserLoginForm
|
from .forms import HostingUserSignupForm, HostingUserLoginForm
|
||||||
from .mixins import ProcessVMSelectionMixin
|
from .mixins import ProcessVMSelectionMixin
|
||||||
|
|
||||||
|
@ -328,6 +329,10 @@ class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin, Detai
|
||||||
permission_required = ['view_hostingorder']
|
permission_required = ['view_hostingorder']
|
||||||
model = HostingOrder
|
model = HostingOrder
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(DetailView, self).get_context_data(**kwargs)
|
||||||
|
print(context)
|
||||||
|
return context
|
||||||
|
|
||||||
class OrdersHostingListView(LoginRequiredMixin, ListView):
|
class OrdersHostingListView(LoginRequiredMixin, ListView):
|
||||||
template_name = "hosting/orders.html"
|
template_name = "hosting/orders.html"
|
||||||
|
@ -396,3 +401,72 @@ class VirtualMachineView(PermissionRequiredMixin, LoginRequiredMixin, UpdateView
|
||||||
email.send()
|
email.send()
|
||||||
|
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
|
|
||||||
|
class HostingBillListView(LoginRequiredMixin, ListView):
|
||||||
|
template_name = "hosting/bills.html"
|
||||||
|
login_url = reverse_lazy('hosting:login')
|
||||||
|
context_object_name = "users"
|
||||||
|
model = StripeCustomer
|
||||||
|
paginate_by = 10
|
||||||
|
ordering = '-id'
|
||||||
|
#TODO show only clients i.e. get_query_set
|
||||||
|
|
||||||
|
class HostingBillDetailView(PermissionRequiredMixin, LoginRequiredMixin, DetailView):
|
||||||
|
template_name = "hosting/bill_detail.html"
|
||||||
|
login_url = reverse_lazy('hosting:login')
|
||||||
|
permission_required = ['view_hostingview']
|
||||||
|
context_object_name = "bill"
|
||||||
|
model = HostingBill
|
||||||
|
|
||||||
|
def get_object(self, queryset=None):
|
||||||
|
#Get HostingBill for primary key (Select from customer users)
|
||||||
|
pk = self.kwargs['pk']
|
||||||
|
return HostingBill.objects.filter(customer__id=pk).first()
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
# Get User
|
||||||
|
user_email = self.object.customer.user.email
|
||||||
|
# Get context
|
||||||
|
context = super(DetailView, self).get_context_data(**kwargs)
|
||||||
|
# Add VMs to context
|
||||||
|
context['vms'] = []
|
||||||
|
|
||||||
|
# Connect to open nebula server
|
||||||
|
client = oca.Client("{0}:{1}".format(settings.OPENNEBULA_USERNAME,
|
||||||
|
settings.OPENNEBULA_PASSWORD),
|
||||||
|
"{protocol}://{domain}:{port}{endpoint}".format(
|
||||||
|
protocol=settings.OPENNEBULA_PROTOCOL,
|
||||||
|
domain=settings.OPENNEBULA_DOMAIN,
|
||||||
|
port=settings.OPENNEBULA_PORT,
|
||||||
|
endpoint=settings.OPENNEBULA_ENDPOINT
|
||||||
|
))
|
||||||
|
# Get open nebula user id for given email
|
||||||
|
user_pool = oca.UserPool(client)
|
||||||
|
user_pool.info()
|
||||||
|
user_id = user_pool.get_by_name('alain').id
|
||||||
|
|
||||||
|
# Get vm_pool for given user_id
|
||||||
|
vm_pool = oca.VirtualMachinePool(client)
|
||||||
|
vm_pool.info(filter=user_id)
|
||||||
|
# Add vm in vm_pool to context
|
||||||
|
for vm in vm_pool:
|
||||||
|
#TODO: Replace with vm plan
|
||||||
|
name = vm.name
|
||||||
|
cores = int(vm.template.vcpu)
|
||||||
|
memory = int(vm.template.memory) / 1024
|
||||||
|
# Check if vm has more than one disk
|
||||||
|
if 'DISK' in vm.template.multiple:
|
||||||
|
disk_size = 0
|
||||||
|
for disk in vm.template.disks:
|
||||||
|
disk_size += int(disk.size) / 1024
|
||||||
|
else:
|
||||||
|
disk_size = int(vm.template.disk.size) / 1024
|
||||||
|
vm = {}
|
||||||
|
vm['name'] = name
|
||||||
|
vm['price'] = 0.6 * disk_size + 2 * memory + 5 * cores
|
||||||
|
vm['disk_size'] = disk_size
|
||||||
|
vm['cores'] = cores
|
||||||
|
vm['memory'] = memory
|
||||||
|
context['vms'].append(vm)
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
Loading…
Reference in a new issue