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…
	
	Add table
		Add a link
		
	
		Reference in a new issue