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 .forms import HostingOrderAdminForm
|
||||
from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder, ManageVM
|
||||
from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder, \
|
||||
ManageVM, HostingBill
|
||||
from .opennebula_functions import HostingManageVMAdmin
|
||||
|
||||
|
||||
|
@ -98,3 +99,4 @@ admin.site.register(HostingOrder, HostingOrderAdmin)
|
|||
admin.site.register(VirtualMachineType)
|
||||
admin.site.register(VirtualMachinePlan, VirtualMachinePlanAdmin)
|
||||
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 oca
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.functional import cached_property
|
||||
|
@ -233,3 +234,18 @@ class ManageVM(models.Model):
|
|||
|
||||
class Meta:
|
||||
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, \
|
||||
OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView,\
|
||||
VirtualMachineView, GenerateVMSSHKeysView, OrdersHostingDeleteView, NotificationsView, \
|
||||
MarkAsReadNotificationView, PasswordResetView, PasswordResetConfirmView, HostingPricingView
|
||||
MarkAsReadNotificationView, PasswordResetView, PasswordResetConfirmView, \
|
||||
HostingPricingView, HostingBillListView, HostingBillDetailView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'index/?$', IndexView.as_view(), name='index'),
|
||||
|
@ -15,6 +16,8 @@ urlpatterns = [
|
|||
url(r'payment/?$', PaymentVMView.as_view(), name='payment'),
|
||||
url(r'orders/?$', OrdersHostingListView.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'my-virtual-machines/?$', VirtualMachinesPlanListView.as_view(), name='virtual_machines'),
|
||||
url(r'my-virtual-machines/(?P<pk>\d+)/?$', VirtualMachineView.as_view(),
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import oca
|
||||
|
||||
from django.shortcuts import render
|
||||
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.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin
|
||||
from utils.mailer import BaseEmail
|
||||
from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder
|
||||
from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder, HostingBill
|
||||
from .forms import HostingUserSignupForm, HostingUserLoginForm
|
||||
from .mixins import ProcessVMSelectionMixin
|
||||
|
||||
|
@ -328,6 +329,10 @@ class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin, Detai
|
|||
permission_required = ['view_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):
|
||||
template_name = "hosting/orders.html"
|
||||
|
@ -396,3 +401,72 @@ class VirtualMachineView(PermissionRequiredMixin, LoginRequiredMixin, UpdateView
|
|||
email.send()
|
||||
|
||||
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