diff --git a/hosting/admin.py b/hosting/admin.py
index b4f1ba24..ee4e7415 100644
--- a/hosting/admin.py
+++ b/hosting/admin.py
@@ -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)
diff --git a/hosting/migrations/0028_managevms.py b/hosting/migrations/0028_managevms.py
new file mode 100644
index 00000000..b71480f2
--- /dev/null
+++ b/hosting/migrations/0028_managevms.py
@@ -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,
+ },
+ ),
+ ]
diff --git a/hosting/migrations/0029_managevm.py b/hosting/migrations/0029_managevm.py
new file mode 100644
index 00000000..946e4264
--- /dev/null
+++ b/hosting/migrations/0029_managevm.py
@@ -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,
+ },
+ ),
+ ]
diff --git a/hosting/migrations/0030_hostingbill.py b/hosting/migrations/0030_hostingbill.py
new file mode 100644
index 00000000..edb01aed
--- /dev/null
+++ b/hosting/migrations/0030_hostingbill.py
@@ -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),
+ ),
+ ]
diff --git a/hosting/models.py b/hosting/models.py
index b24dc855..a27fb487 100644
--- a/hosting/models.py
+++ b/hosting/models.py
@@ -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)
+
diff --git a/hosting/templates/hosting/bill_detail.html b/hosting/templates/hosting/bill_detail.html
new file mode 100644
index 00000000..1904bf82
--- /dev/null
+++ b/hosting/templates/hosting/bill_detail.html
@@ -0,0 +1,93 @@
+{% extends "hosting/base_short.html" %}
+{% load staticfiles bootstrap3 %}
+{% load i18n %}
+{% block content %}
+
+
{{ bill }}
+
+ {# Adress bar #}
+
+
+
{% trans "Invoice"%} {% trans "Order #"%} {{bill.id}}
+
+
+
+
+
+
+ {{bill.customer.user.name}}
+ {{bill.billing_address.street_address}},{{bill.billing_address.postal_code}}
+ {{bill.billing_address.city}}, {{bill.billing_address.country}}.
+
+
+
+
+ {% trans "ungleich GmbH" %}
+ {% trans "buchhaltung@ungleich.ch" %}
+ {% trans "Hauptstrasse 14"%}
+ {% trans "CH-8775 Luchsingen"%}
+ {% trans "Mwst-Nummer: CHE-109.549.333 MWST"%}
+
+
+
+
+
+
+ {# Bill header #}
+
+
+ Name
+ Cores
+ Memory
+ Disk Size
+ Price
+
+
+
+ {# Bill items#}
+ {% for vm in vms %}
+
+ {{ vm.name }}
+ {{ vm.cores }}
+ {{ vm.memory }}
+ {{ vm.disk_size }}
+ {{ vm.price }}
+
+
+ {% endfor %}
+ {# Bill total#}
+
+ {% trans "Total:" %}
+
+
+ {% trans "Brutto" %}
+ {% trans "Netto" %}
+
+
+
+
+ {# Bill Footer #}
+
+ {% trans "Alles Preise in CHF mit 8% Mehrwertsteuer." %}
+ {% trans "Betrag zahlbar innerhalb von 30 Tagen ab Rechnungseingang." %}
+ {% trans "Kontoverbindung:" %}
+
+
+ {% trans "IBAN:" %}
+
+
+ {% trans "BIC:" %}
+
+
+
+
+ {% trans "CH02 ............" %}
+
+
+ {% trans "POFICHBEXXX" %}
+
+
+
+
+{% endblock %}
+
diff --git a/hosting/templates/hosting/bills.html b/hosting/templates/hosting/bills.html
new file mode 100644
index 00000000..cc1883fb
--- /dev/null
+++ b/hosting/templates/hosting/bills.html
@@ -0,0 +1,60 @@
+{% extends "hosting/base_short.html" %}
+{% load staticfiles bootstrap3 %}
+{% load i18n %}
+
+{% block content %}
+
+
+
+
+
+
+ {% trans "Customers"%}
+
+
+
+ {% trans "Name"%}
+ {% trans "Email"%}
+
+
+
+
+ {% for user in users %}
+
+ {{ user.name}}
+ {{ user.email}} CHF
+
+ {% trans "View Bill"%}
+
+
+
+ {% endfor %}
+
+
+
+
+ {% if is_paginated %}
+
+ {% endif %}
+
+
+
+
+
+
+
+
+{% endblock %}
diff --git a/hosting/urls.py b/hosting/urls.py
index 5ceeba97..1df6be9e 100644
--- a/hosting/urls.py
+++ b/hosting/urls.py
@@ -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\d+)/?$', OrdersHostingDetailView.as_view(), name='orders'),
+ url(r'bills/?$', HostingBillListView.as_view(), name='bills'),
+ url(r'bills/(?P\d+)/?$', HostingBillDetailView.as_view(), name='bills'),
url(r'cancel_order/(?P\d+)/?$', OrdersHostingDeleteView.as_view(), name='delete_order'),
url(r'my-virtual-machines/?$', VirtualMachinesPlanListView.as_view(), name='virtual_machines'),
url(r'my-virtual-machines/(?P\d+)/?$', VirtualMachineView.as_view(),
diff --git a/hosting/views.py b/hosting/views.py
index 6b21c91b..c9634200 100644
--- a/hosting/views.py
+++ b/hosting/views.py
@@ -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