diff --git a/hosting/managers.py b/hosting/managers.py
new file mode 100644
index 00000000..961b2c6f
--- /dev/null
+++ b/hosting/managers.py
@@ -0,0 +1,8 @@
+from django.db import models
+
+
+class VMPlansManager(models.Manager):
+
+ def active(self, user, **kwargs):
+ return self.select_related('hostingorder__customer__user').\
+ filter(hostingorder__customer__user=user, hostingorder__approved=True, **kwargs)
diff --git a/hosting/migrations/0012_auto_20160501_1850.py b/hosting/migrations/0012_auto_20160501_1850.py
new file mode 100644
index 00000000..3ef15a46
--- /dev/null
+++ b/hosting/migrations/0012_auto_20160501_1850.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2016-05-01 18:50
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('hosting', '0011_auto_20160426_0555'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='hostingorder',
+ name='cc_brand',
+ field=models.CharField(default='Visa', max_length=10),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='hostingorder',
+ name='last4',
+ field=models.CharField(default=1111, max_length=4),
+ preserve_default=False,
+ ),
+ ]
diff --git a/hosting/models.py b/hosting/models.py
index f3e2a178..edee580d 100644
--- a/hosting/models.py
+++ b/hosting/models.py
@@ -2,10 +2,13 @@ import json
from django.db import models
from django.utils.translation import ugettext_lazy as _
+from django.utils.functional import cached_property
from django.core import serializers
from membership.models import StripeCustomer
from utils.models import BillingAddress
+from .managers import VMPlansManager
+
class RailsBetaUser(models.Model):
email = models.EmailField(unique=True)
@@ -81,6 +84,12 @@ class VirtualMachinePlan(models.Model):
vm_type = models.ForeignKey(VirtualMachineType)
price = models.FloatField()
+ objects = VMPlansManager()
+
+ @cached_property
+ def hosting_company_name(self):
+ return self.vm_type.get_hosting_company_display()
+
@classmethod
def create(cls, data, user):
instance = cls.objects.create(**data)
@@ -88,13 +97,23 @@ class VirtualMachinePlan(models.Model):
class HostingOrder(models.Model):
+
+ ORDER_APPROVED_STATUS = 'Approved'
+ ORDER_DECLINED_STATUS = 'Declined'
+
VMPlan = models.OneToOneField(VirtualMachinePlan)
customer = models.ForeignKey(StripeCustomer)
billing_address = models.ForeignKey(BillingAddress)
created_at = models.DateTimeField(auto_now_add=True)
approved = models.BooleanField(default=False)
+ last4 = models.CharField(max_length=4)
+ cc_brand = models.CharField(max_length=10)
stripe_charge_id = models.CharField(max_length=100, null=True)
+ @cached_property
+ def status(self):
+ return self.ORDER_APPROVED_STATUS if self.approved else self.ORDER_DECLINED_STATUS
+
@classmethod
def create(cls, VMPlan=None, customer=None, billing_address=None):
instance = cls.objects.create(VMPlan=VMPlan, customer=customer,
@@ -107,6 +126,8 @@ class HostingOrder(models.Model):
def set_stripe_charge(self, stripe_charge):
self.stripe_charge_id = stripe_charge.id
+ self.last4 = stripe_charge.source.last4
+ self.cc_brand = stripe_charge.source.brand
self.save()
diff --git a/hosting/static/hosting/css/commons.css b/hosting/static/hosting/css/commons.css
new file mode 100644
index 00000000..e77ddfa2
--- /dev/null
+++ b/hosting/static/hosting/css/commons.css
@@ -0,0 +1,15 @@
+.dashboard-container {
+ padding-top:5%; padding-bottom: 11%;
+}
+
+.borderless td {
+ border: none !important;
+}
+.borderless thead {
+}
+
+.borderless tbody:before {
+ content: "-";
+ display: block;
+ color: transparent;
+}
diff --git a/hosting/static/hosting/css/order.css b/hosting/static/hosting/css/order.css
new file mode 100644
index 00000000..8e0c5919
--- /dev/null
+++ b/hosting/static/hosting/css/order.css
@@ -0,0 +1,17 @@
+.order-detail-container {padding-top:5%; padding-bottom: 11%;}
+
+.order-detail-container .invoice-title h2, .invoice-title h3 {
+ display: inline-block;
+}
+
+.order-detail-container .table > tbody > tr > .no-line {
+ border-top: none;
+}
+
+.order-detail-container .table > thead > tr > .no-line {
+ border-bottom: none;
+}
+
+.order-detail-container .table > tbody > tr > .thick-line {
+ border-top: 2px solid;
+}
diff --git a/hosting/static/hosting/css/orders.css b/hosting/static/hosting/css/orders.css
new file mode 100644
index 00000000..64e95328
--- /dev/null
+++ b/hosting/static/hosting/css/orders.css
@@ -0,0 +1,5 @@
+.orders-container {padding-top:5%; padding-bottom: 11%;}
+
+.orders-container .table > tbody > tr > td {
+ vertical-align: middle;
+}
\ No newline at end of file
diff --git a/hosting/templates/hosting/base_short.html b/hosting/templates/hosting/base_short.html
index fabfa030..266ee484 100644
--- a/hosting/templates/hosting/base_short.html
+++ b/hosting/templates/hosting/base_short.html
@@ -18,6 +18,9 @@
+
+
+
@@ -49,30 +52,51 @@
-
+
diff --git a/hosting/templates/hosting/invoice.html b/hosting/templates/hosting/order_detail.html
similarity index 51%
rename from hosting/templates/hosting/invoice.html
rename to hosting/templates/hosting/order_detail.html
index 302028fe..4d5dcc18 100644
--- a/hosting/templates/hosting/invoice.html
+++ b/hosting/templates/hosting/order_detail.html
@@ -2,55 +2,37 @@
{% load staticfiles bootstrap3 %}
{% block content %}
-
-
-
+
-
Invoice Order # {{order.id}}
+ Invoice Order # {{object.id}}
Billed To:
- John Smith
- 1234 Main
- Apt. 4B
- Springfield, ST 54321
+ {{user.name}}
+ {{object.billing_address.street_address}},{{order.billing_address.postal_code}}
+ {{object.billing_address.city}}, {{object.billing_address.country}}.
Order Date:
- {{order.created_at}}
+ {{object.created_at}}
+ Status:
+ {{object.status}}
+
Payment Method:
- {{brand}} ending **** {{last4}}
+ {{object.cc_brand}} ending **** {{object.last4}}
{{user.email}}
@@ -63,15 +45,15 @@
Order summary
-
Type {{request.session.vm_specs.hosting_company_name}}
+
Type {{object.VMPlan.hosting_company_name}}
-
Cores {{request.session.vm_specs.cores}}
+
Cores {{object.VMPlan.cores}}
-
Memory {{request.session.vm_specs.memory}} GiB
+
Memory {{object.VMPlan.memory}} GiB
-
Disk space {{request.session.vm_specs.disk_size}} GiB
+
Disk space {{object.VMPlan.disk_size}} GiB
-
Total {{request.session.vm_specs.final_price}} CHF
+
Total {{object.VMPlan.price}} CHF
diff --git a/hosting/templates/hosting/orders.html b/hosting/templates/hosting/orders.html
index df5e6216..819dec4d 100644
--- a/hosting/templates/hosting/orders.html
+++ b/hosting/templates/hosting/orders.html
@@ -1,31 +1,75 @@
{% extends "hosting/base_short.html" %}
{% load staticfiles bootstrap3 %}
{% block content %}
+
+
+
-
+
-
- My orders.
+
+ My Orders
+
#
Date
Amount
Status
+
{% for order in orders %}
- {{order.id}}
+ {{order.id}}
{{order.created_at}}
- {{order.VMPlan.price}}
- {{order.approved}}
-
+ {{order.VMPlan.price}} CHF
+ {% if order.approved %}
+ Approved
+ {% else%}
+ Declined
+ {% endif%}
+
+
+ View Detail
+
+
{% endfor %}
+
+ {% if is_paginated %}
+
+ {% endif %}
+
diff --git a/hosting/templates/hosting/virtual_machines.html b/hosting/templates/hosting/virtual_machines.html
new file mode 100644
index 00000000..47eb23b1
--- /dev/null
+++ b/hosting/templates/hosting/virtual_machines.html
@@ -0,0 +1,56 @@
+{% extends "hosting/base_short.html" %}
+{% load staticfiles bootstrap3 %}
+{% block content %}
+
+
+
+
+
+ Virtual Machines
+
+
+
+ ID
+ Type
+ Amount
+
+
+
+
+ {% for vm in vms %}
+
+ {{vm.id}}
+ {{vm.hosting_company_name}}
+ {{vm.price}} CHF
+
+ View Detail
+
+
+ {% endfor %}
+
+
+
+ {% if is_paginated %}
+
+ {% endif %}
+
+
+
+
+
+
+
+
+{%endblock%}
\ No newline at end of file
diff --git a/hosting/urls.py b/hosting/urls.py
index 9f18e836..31a3eaea 100644
--- a/hosting/urls.py
+++ b/hosting/urls.py
@@ -1,19 +1,20 @@
from django.conf.urls import url
from .views import DjangoHostingView, RailsHostingView, PaymentVMView, \
- NodeJSHostingView, LoginView, SignupView, IndexView, \
- InvoiceVMView, OrdersHostingView
+ NodeJSHostingView, LoginView, SignupView, IndexView, \
+ OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView
urlpatterns = [
url(r'index/?$', IndexView.as_view(), name='index'),
url(r'django/?$', DjangoHostingView.as_view(), name='djangohosting'),
url(r'nodejs/?$', NodeJSHostingView.as_view(), name='nodejshosting'),
- url(r'rails/?$', RailsHostingView.as_view(), name='railshosting'),
+ url(r'rails/?$', RailsHostingView.as_view(), name='railshosting'),
url(r'payment/?$', PaymentVMView.as_view(), name='payment'),
- url(r'invoice/?$', InvoiceVMView.as_view(), name='invoice'),
- url(r'orders/?$', OrdersHostingView.as_view(), name='orders'),
- url(r'login/?$', LoginView.as_view(), name='login'),
+ url(r'orders/?$', OrdersHostingListView.as_view(), name='orders'),
+ url(r'orders/(?P\d+)/?$', OrdersHostingDetailView.as_view(), name='orders'),
+ url(r'my-virtual-machines/?$', VirtualMachinesPlanListView.as_view(), name='virtual_machines'),
+ url(r'login/?$', LoginView.as_view(), name='login'),
url(r'signup/?$', SignupView.as_view(), name='signup'),
url(r'^logout/?$', 'django.contrib.auth.views.logout',
- {'next_page': '/ungleich_page'}, name='logout')
+ {'next_page': '/ungleich_page'}, name='logout')
]
diff --git a/hosting/views.py b/hosting/views.py
index 65bbbcf3..bfdb5d13 100644
--- a/hosting/views.py
+++ b/hosting/views.py
@@ -2,16 +2,12 @@
from django.shortcuts import get_object_or_404, render
from django.core.urlresolvers import reverse_lazy, reverse
from django.contrib.auth.mixins import LoginRequiredMixin
-from django.contrib.auth.decorators import login_required
-from django.utils.decorators import method_decorator
-from django.views.generic import View, CreateView, FormView
-from django.shortcuts import redirect
+from django.views.generic import View, CreateView, FormView, ListView, DetailView
from django.http import HttpResponseRedirect
from django.contrib.auth import authenticate, login
from django.conf import settings
-from membership.forms import PaymentForm
from membership.models import CustomUser, StripeCustomer
from utils.stripe_utils import StripeUtils
from utils.forms import BillingAddressForm
@@ -21,7 +17,6 @@ from .forms import HostingUserSignupForm, HostingUserLoginForm
from .mixins import ProcessVMSelectionMixin
-
class DjangoHostingView(ProcessVMSelectionMixin, View):
template_name = "hosting/django.html"
@@ -217,60 +212,38 @@ class PaymentVMView(FormView):
'order': order.id,
'billing_address': billing_address.id
})
- return HttpResponseRedirect(reverse('hosting:invoice'))
+ return HttpResponseRedirect(reverse('hosting:orders', kwargs={'pk': order.id}))
else:
return self.form_invalid(form)
-class InvoiceVMView(LoginRequiredMixin, View):
- template_name = "hosting/invoice.html"
+class OrdersHostingDetailView(LoginRequiredMixin, DetailView):
+ template_name = "hosting/order_detail.html"
login_url = reverse_lazy('hosting:login')
-
- def get_context_data(self, **kwargs):
- charge = self.request.session.get('charge')
- order_id = self.request.session.get('order')
- billing_address_id = self.request.session.get('billing_address')
- last4 = charge.get('source').get('last4')
- brand = charge.get('source').get('brand')
-
- order = get_object_or_404(HostingOrder, pk=order_id)
- billing_address = get_object_or_404(BillingAddress, pk=billing_address_id)
-
- if not charge:
- return
-
- context = {
- 'last4': last4,
- 'brand': brand,
- 'order': order,
- 'billing_address': billing_address,
- }
- return context
-
- def get(self, request, *args, **kwargs):
-
- context = self.get_context_data()
-
- return render(request, self.template_name, context)
+ model = HostingOrder
-class OrdersHostingView(LoginRequiredMixin, View):
+class OrdersHostingListView(LoginRequiredMixin, ListView):
template_name = "hosting/orders.html"
login_url = reverse_lazy('hosting:login')
+ context_object_name = "orders"
+ model = HostingOrder
+ paginate_by = 10
- def get_context_data(self, **kwargs):
+ def get_queryset(self):
user = self.request.user
- orders = HostingOrder.objects.filter(customer__user=user)
- context = {
- 'orders':orders
- }
-
- return context
-
- def get(self, request, *args, **kwargs):
-
- context = self.get_context_data()
-
- return render(request, self.template_name, context)
+ self.queryset = HostingOrder.objects.filter(customer__user=user)
+ return super(OrdersHostingListView, self).get_queryset()
+class VirtualMachinesPlanListView(LoginRequiredMixin, ListView):
+ template_name = "hosting/virtual_machines.html"
+ login_url = reverse_lazy('hosting:login')
+ context_object_name = "vms"
+ model = VirtualMachinePlan
+ paginate_by = 10
+
+ def get_queryset(self):
+ user = self.request.user
+ self.queryset = VirtualMachinePlan.objects.active(user)
+ return super(VirtualMachinesPlanListView, self).get_queryset()