From 0fc823a305d7146b0d9ad98a8ffccfddd9a34b8f Mon Sep 17 00:00:00 2001
From: Jan Dittberner <jan@dittberner.info>
Date: Sun, 25 Jan 2015 15:15:39 +0100
Subject: [PATCH] implement adding options to hosting packages

- fix unique constraints on CustomerDiskSpaceOption and
  CustomerUserDatabaseOption to allow multiple options from the same template
  for hosting packages
- fix disk space calculation in CustomerHostingPackage
- implement hostingpackages forms AddDiskspaceOptionForm, AddMailboxOptionForm,
  AddUserDatabaseOptionForm
- implement hostingpackages.views.AddHostingOption
- add new URL pattern add_hosting_option to hostingpackages.urls
- add template hostingpackages/add_hosting_option.html
- link items on hostingpackages/customerhostingpackage_option_choices.html to
  add_hosting_option
---
 gnuviechadmin/hostingpackages/forms.py        | 83 +++++++++++++++++-
 .../migrations/0005_auto_20150125_1508.py     | 22 +++++
 .../migrations/0006_auto_20150125_1510.py     | 22 +++++
 gnuviechadmin/hostingpackages/models.py       |  8 +-
 gnuviechadmin/hostingpackages/urls.py         |  9 +-
 gnuviechadmin/hostingpackages/views.py        | 87 ++++++++++++++++++-
 .../hostingpackages/add_hosting_option.html   |  8 ++
 ...customerhostingpackage_option_choices.html |  4 +-
 8 files changed, 229 insertions(+), 14 deletions(-)
 create mode 100644 gnuviechadmin/hostingpackages/migrations/0005_auto_20150125_1508.py
 create mode 100644 gnuviechadmin/hostingpackages/migrations/0006_auto_20150125_1510.py
 create mode 100644 gnuviechadmin/templates/hostingpackages/add_hosting_option.html

diff --git a/gnuviechadmin/hostingpackages/forms.py b/gnuviechadmin/hostingpackages/forms.py
index 727580d..b424ec7 100644
--- a/gnuviechadmin/hostingpackages/forms.py
+++ b/gnuviechadmin/hostingpackages/forms.py
@@ -14,7 +14,12 @@ from crispy_forms.layout import (
     Submit,
 )
 
-from .models import CustomerHostingPackage
+from .models import (
+    CustomerDiskSpaceOption,
+    CustomerHostingPackage,
+    CustomerMailboxOption,
+    CustomerUserDatabaseOption,
+)
 
 
 class CreateCustomerHostingPackageForm(forms.ModelForm):
@@ -68,3 +73,79 @@ class CreateHostingPackageForm(forms.ModelForm):
             'description',
             Submit('submit', _('Add Hosting Package')),
         )
+
+
+class AddDiskspaceOptionForm(forms.ModelForm):
+    class Meta:
+        model = CustomerDiskSpaceOption
+        fields = ['diskspace', 'diskspace_unit']
+
+    def __init__(self, *args, **kwargs):
+        self.hostingpackage = kwargs.pop('hostingpackage')
+        self.option_template = kwargs.pop('option_template')
+        super(AddDiskspaceOptionForm, self).__init__(*args, **kwargs)
+        self.helper = FormHelper()
+        self.helper.form_action = reverse(
+            'add_hosting_option',
+            kwargs={
+                'package': self.hostingpackage.id,
+                'type': 'diskspace',
+                'optionid': self.option_template.id,
+            })
+        self.helper.add_input(Submit('submit', _('Add disk space option')))
+
+    def save(self, commit=True):
+        self.instance.hosting_package = self.hostingpackage
+        self.instance.template = self.option_template
+        return super(AddDiskspaceOptionForm, self).save(commit=True)
+
+
+class AddMailboxOptionForm(forms.ModelForm):
+    class Meta:
+        model = CustomerMailboxOption
+        fields = ['number']
+
+    def __init__(self, *args, **kwargs):
+        self.hostingpackage = kwargs.pop('hostingpackage')
+        self.option_template = kwargs.pop('option_template')
+        super(AddMailboxOptionForm, self).__init__(*args, **kwargs)
+        self.helper = FormHelper()
+        self.helper.form_action = reverse(
+            'add_hosting_option',
+            kwargs={
+                'package': self.hostingpackage.id,
+                'type': 'mailboxes',
+                'optionid': self.option_template.id,
+            })
+        self.helper.add_input(Submit('submit', _('Add mailbox option')))
+
+    def save(self, commit=True):
+        self.instance.hosting_package = self.hostingpackage
+        self.instance.template = self.option_template
+        return super(AddMailboxOptionForm, self).save(commit=True)
+
+
+class AddUserDatabaseOptionForm(forms.ModelForm):
+    class Meta:
+        model = CustomerUserDatabaseOption
+        fields = ['number']
+
+    def __init__(self, *args, **kwargs):
+        self.hostingpackage = kwargs.pop('hostingpackage')
+        self.option_template = kwargs.pop('option_template')
+        super(AddUserDatabaseOptionForm, self).__init__(*args, **kwargs)
+        self.helper = FormHelper()
+        self.helper.form_action = reverse(
+            'add_hosting_option',
+            kwargs={
+                'package': self.hostingpackage.id,
+                'type': 'databases',
+                'optionid': self.option_template.id,
+            })
+        self.helper.add_input(Submit('submit', _('Add database option')))
+
+    def save(self, commit=True):
+        self.instance.hosting_package = self.hostingpackage
+        self.instance.template = self.option_template
+        self.instance.db_type = self.option_template.db_type
+        return super(AddUserDatabaseOptionForm, self).save(commit=True)
diff --git a/gnuviechadmin/hostingpackages/migrations/0005_auto_20150125_1508.py b/gnuviechadmin/hostingpackages/migrations/0005_auto_20150125_1508.py
new file mode 100644
index 0000000..8bade76
--- /dev/null
+++ b/gnuviechadmin/hostingpackages/migrations/0005_auto_20150125_1508.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('hostingpackages', '0004_customerhostingpackagedomain'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='diskspaceoption',
+            options={},
+        ),
+        migrations.AlterUniqueTogether(
+            name='customerdiskspaceoption',
+            unique_together=set([]),
+        ),
+    ]
diff --git a/gnuviechadmin/hostingpackages/migrations/0006_auto_20150125_1510.py b/gnuviechadmin/hostingpackages/migrations/0006_auto_20150125_1510.py
new file mode 100644
index 0000000..17f24c9
--- /dev/null
+++ b/gnuviechadmin/hostingpackages/migrations/0006_auto_20150125_1510.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('hostingpackages', '0005_auto_20150125_1508'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='userdatabaseoption',
+            options={},
+        ),
+        migrations.AlterUniqueTogether(
+            name='customeruserdatabaseoption',
+            unique_together=set([]),
+        ),
+    ]
diff --git a/gnuviechadmin/hostingpackages/models.py b/gnuviechadmin/hostingpackages/models.py
index d45405e..2e5ff43 100644
--- a/gnuviechadmin/hostingpackages/models.py
+++ b/gnuviechadmin/hostingpackages/models.py
@@ -80,7 +80,6 @@ class DiskSpaceOptionBase(models.Model):
     class Meta:
         abstract = True
         ordering = ['diskspace_unit', 'diskspace']
-        unique_together = ['diskspace', 'diskspace_unit']
         verbose_name = _('Disk space option')
         verbose_name_plural = _('Disk space options')
 
@@ -95,6 +94,8 @@ class DiskSpaceOption(DiskSpaceOptionBase, HostingOption):
     existing hosting packages.
 
     """
+    class Meta:
+        unique_together = ['diskspace', 'diskspace_unit']
 
 
 @python_2_unicode_compatible
@@ -107,7 +108,6 @@ class UserDatabaseOptionBase(models.Model):
     class Meta:
         abstract = True
         ordering = ['db_type', 'number']
-        unique_together = ['number', 'db_type']
         verbose_name = _('Database option')
         verbose_name_plural = _('Database options')
 
@@ -127,6 +127,8 @@ class UserDatabaseOption(UserDatabaseOptionBase, HostingOption):
     hosting packages.
 
     """
+    class Meta:
+        unique_together = ['number', 'db_type']
 
 
 @python_2_unicode_compatible
@@ -267,7 +269,7 @@ class CustomerHostingPackage(HostingPackageBase):
         options = CustomerDiskSpaceOption.objects.filter(hosting_package=self)
         for option in options:
             if option.diskspace_unit == min_unit:
-                diskspace += option.disk_space
+                diskspace += option.diskspace
             elif option.diskspace_unit > min_unit:
                 diskspace += (
                     DISK_SPACE_FACTORS[option.diskspace_unit][min_unit] *
diff --git a/gnuviechadmin/hostingpackages/urls.py b/gnuviechadmin/hostingpackages/urls.py
index 222ae5d..2f9b810 100644
--- a/gnuviechadmin/hostingpackages/urls.py
+++ b/gnuviechadmin/hostingpackages/urls.py
@@ -7,6 +7,7 @@ from __future__ import absolute_import, unicode_literals
 from django.conf.urls import patterns, url
 
 from .views import (
+    AddHostingOption,
     AllCustomerHostingPackageList,
     CreateCustomerHostingPackage,
     CreateHostingPackage,
@@ -26,9 +27,9 @@ urlpatterns = patterns(
         CustomerHostingPackageDetails.as_view(),
         name='hosting_package_details'),
     url(r'^allpackages/',
-        AllCustomerHostingPackageList.as_view(),
-        name='all_hosting_packages'),
+        AllCustomerHostingPackageList.as_view(), name='all_hosting_packages'),
     url(r'^(?P<pk>\d+)/option-choices$',
-        HostingOptionChoices.as_view(),
-        name='hosting_option_choices'),
+        HostingOptionChoices.as_view(), name='hosting_option_choices'),
+    url(r'^(?P<package>\d+)/add-option/(?P<type>\w+)/(?P<optionid>\d+)$',
+        AddHostingOption.as_view(), name='add_hosting_option'),
 )
diff --git a/gnuviechadmin/hostingpackages/views.py b/gnuviechadmin/hostingpackages/views.py
index 0cb3ccd..455e717 100644
--- a/gnuviechadmin/hostingpackages/views.py
+++ b/gnuviechadmin/hostingpackages/views.py
@@ -5,13 +5,17 @@ This module defines views related to hosting packages.
 from __future__ import absolute_import, unicode_literals
 
 from django.conf import settings
+from django.http import Http404
 from django.shortcuts import redirect, get_object_or_404
 from django.utils.translation import ugettext as _
 from django.views.generic import (
     DetailView,
     ListView,
 )
-from django.views.generic.edit import CreateView
+from django.views.generic.edit import (
+    CreateView,
+    FormView,
+)
 from django.contrib import messages
 from django.contrib.auth import get_user_model
 
@@ -23,6 +27,9 @@ from braces.views import (
 from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin
 
 from .forms import (
+    AddDiskspaceOptionForm,
+    AddMailboxOptionForm,
+    AddUserDatabaseOptionForm,
     CreateCustomerHostingPackageForm,
     CreateHostingPackageForm,
 )
@@ -137,11 +144,83 @@ class HostingOptionChoices(
             'customer': self.get_object().customer,
             'hosting_options': (
                 (_('Disk space'),
-                 DiskSpaceOption.objects.all()),
+                 [(option, 'diskspace') for option in
+                  DiskSpaceOption.objects.all()]),
                 (_('Mailboxes'),
-                 MailboxOption.objects.all()),
+                 [(option, 'mailboxes') for option in
+                  MailboxOption.objects.all()]),
                 (_('Databases'),
-                 UserDatabaseOption.objects.all())
+                 [(option, 'databases') for option in
+                  UserDatabaseOption.objects.all()]),
             ),
         })
         return context
+
+
+class AddHostingOption(
+    LoginRequiredMixin, StaffuserRequiredMixin, FormView
+):
+    template_name = 'hostingpackages/add_hosting_option.html'
+
+    def get_form_class(self):
+        optiontype = self.kwargs['type']
+        if optiontype == 'diskspace':
+            return AddDiskspaceOptionForm
+        elif optiontype == 'mailboxes':
+            return AddMailboxOptionForm
+        elif optiontype == 'databases':
+            return AddUserDatabaseOptionForm
+        raise Http404()
+
+    def get_hosting_package(self):
+        return get_object_or_404(
+            CustomerHostingPackage, pk=int(self.kwargs['package']))
+
+    def get_option_template(self):
+        optiontype = self.kwargs['type']
+        optionid = int(self.kwargs['optionid'])
+        if optiontype == 'diskspace':
+            return get_object_or_404(DiskSpaceOption, pk=optionid)
+        elif optiontype == 'mailboxes':
+            return get_object_or_404(MailboxOption, pk=optionid)
+        elif optiontype == 'databases':
+            return get_object_or_404(UserDatabaseOption, pk=optionid)
+        raise Http404()
+
+    def get_form_kwargs(self):
+        kwargs = super(AddHostingOption, self).get_form_kwargs()
+        kwargs['hostingpackage'] = self.get_hosting_package()
+        kwargs['option_template'] = self.get_option_template()
+        return kwargs
+
+    def get_initial(self):
+        initial = super(AddHostingOption, self).get_initial()
+        template = self.get_option_template()
+        if type(template) == DiskSpaceOption:
+            initial.update({
+                'diskspace': template.diskspace,
+                'diskspace_unit': template.diskspace_unit,
+            })
+        elif type(template) == MailboxOption:
+            initial['number'] = template.number
+        elif type(template) == UserDatabaseOption:
+            initial['number'] = template.number
+        else:
+            raise Http404()
+        return initial
+
+    def get_context_data(self, **kwargs):
+        context = super(AddHostingOption, self).get_context_data(**kwargs)
+        context['option_template'] = self.get_option_template()
+        return context
+
+    def form_valid(self, form):
+        option = form.save()
+        hosting_package = self.get_hosting_package()
+        messages.success(
+            self.request,
+            _("Successfully added option {option} to hosting package "
+              "{package}.").format(
+                  option=option, package=hosting_package.name)
+        )
+        return redirect(hosting_package)
diff --git a/gnuviechadmin/templates/hostingpackages/add_hosting_option.html b/gnuviechadmin/templates/hostingpackages/add_hosting_option.html
new file mode 100644
index 0000000..76b013e
--- /dev/null
+++ b/gnuviechadmin/templates/hostingpackages/add_hosting_option.html
@@ -0,0 +1,8 @@
+{% extends "hostingpackages/base.html" %}
+{% load i18n crispy_forms_tags %}
+{% block title %}{{ block.super }} - {% blocktrans with package=hostingpackage.name full_name=customer.get_full_name %}Add Option to Hosting Package {{ package }} of Customer {{ full_name }}{% endblocktrans %}{% endblock title %}
+{% block page_title %}{% blocktrans with package=hostingpackage.name full_name=customer.get_full_name %}Add Option to Hosting Package {{ package }} of Customer {{ full_name }}{% endblocktrans %}{% endblock page_title %}
+
+{% block content %}
+{% crispy form %}
+{% endblock content %}
diff --git a/gnuviechadmin/templates/hostingpackages/customerhostingpackage_option_choices.html b/gnuviechadmin/templates/hostingpackages/customerhostingpackage_option_choices.html
index bd1e92f..51c71a4 100644
--- a/gnuviechadmin/templates/hostingpackages/customerhostingpackage_option_choices.html
+++ b/gnuviechadmin/templates/hostingpackages/customerhostingpackage_option_choices.html
@@ -12,8 +12,8 @@
     <div class="panel panel-default">
       <div class="panel-heading">{{ label }}</div>
       <ul class="list-group">
-        {% for item in items %}
-        <li class="list-group-item"><a href="#"><i class="glyphicon glyphicon-plus-sign"></i> {{ item }}</a></li>
+        {% for item, option_type in items %}
+        <li class="list-group-item"><a href="{% url 'add_hosting_option' package=hostingpackage.id type=option_type optionid=item.id %}"><i class="glyphicon glyphicon-plus-sign"></i> {{ item }}</a></li>
         {% endfor %}
       </ul>
     </div>