Merge branch 'master' into 5151/gdpr_modal
This commit is contained in:
commit
1feacc1770
39 changed files with 1391 additions and 390 deletions
|
|
@ -1,8 +1,8 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from .models import HostingOrder, HostingBill, HostingPlan
|
||||
|
||||
from .models import HostingOrder, HostingBill, HostingPlan, GenericProduct
|
||||
|
||||
admin.site.register(HostingOrder)
|
||||
admin.site.register(HostingBill)
|
||||
admin.site.register(HostingPlan)
|
||||
admin.site.register(GenericProduct)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
from membership.models import CustomUser
|
||||
from utils.hosting_utils import get_all_public_keys
|
||||
from .models import UserHostingKey
|
||||
from .models import UserHostingKey, GenericProduct
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -52,6 +52,93 @@ class HostingUserLoginForm(forms.Form):
|
|||
raise forms.ValidationError(_("User does not exist"))
|
||||
|
||||
|
||||
class ProductModelChoiceField(forms.ModelChoiceField):
|
||||
def label_from_instance(self, obj):
|
||||
return obj.product_name
|
||||
|
||||
|
||||
class GenericPaymentForm(forms.Form):
|
||||
product_name = ProductModelChoiceField(
|
||||
queryset=GenericProduct.objects.all().order_by('product_name'),
|
||||
empty_label=_("Choose a product"),
|
||||
)
|
||||
amount = forms.FloatField(
|
||||
widget=forms.TextInput(
|
||||
attrs={'placeholder': _('Amount in CHF'),
|
||||
'readonly': 'readonly', }
|
||||
),
|
||||
max_value=999999,
|
||||
min_value=1,
|
||||
label=_('Amount in CHF')
|
||||
)
|
||||
recurring = forms.BooleanField(required=False,
|
||||
label=_("Recurring monthly"), )
|
||||
description = forms.CharField(
|
||||
widget=forms.Textarea(attrs={'style': "height: 60px;"}),
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = GenericProduct
|
||||
fields = ['product_name', 'amount', 'recurring', 'description']
|
||||
|
||||
def clean_amount(self):
|
||||
amount = self.cleaned_data.get('amount')
|
||||
if (float(self.cleaned_data.get('product_name').get_actual_price()) !=
|
||||
amount):
|
||||
raise forms.ValidationError(_("Amount field does not match"))
|
||||
return amount
|
||||
|
||||
def clean_recurring(self):
|
||||
recurring = self.cleaned_data.get('recurring')
|
||||
if (self.cleaned_data.get('product_name').product_is_subscription !=
|
||||
(True if recurring else False)):
|
||||
raise forms.ValidationError(_("Recurring field does not match"))
|
||||
return recurring
|
||||
|
||||
|
||||
class ProductPaymentForm(GenericPaymentForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
product_id = kwargs.pop('product_id', None)
|
||||
if product_id is not None:
|
||||
self.product = GenericProduct.objects.get(id=product_id)
|
||||
super(ProductPaymentForm, self).__init__(*args, **kwargs)
|
||||
self.fields['product_name'] = forms.CharField(
|
||||
widget=forms.TextInput(
|
||||
attrs={'placeholder': _('Product name'),
|
||||
'readonly': 'readonly'}
|
||||
)
|
||||
)
|
||||
if self.product.product_is_subscription:
|
||||
self.fields['amount'].label = "{amt} ({payment_type})".format(
|
||||
amt=_('Amount in CHF'),
|
||||
payment_type=_('Monthly subscription')
|
||||
)
|
||||
else:
|
||||
self.fields['amount'].label = "{amt} ({payment_type})".format(
|
||||
amt=_('Amount in CHF'),
|
||||
payment_type=_('One time payment')
|
||||
)
|
||||
self.fields['recurring'].widget = forms.HiddenInput()
|
||||
self.fields['product_name'].widget.attrs['class'] = 'input-no-border'
|
||||
self.fields['amount'].widget.attrs['class'] = 'input-no-border'
|
||||
self.fields['description'].widget.attrs['class'] = 'input-no-border'
|
||||
|
||||
def clean_amount(self):
|
||||
amount = self.cleaned_data.get('amount')
|
||||
if (self.product is None or
|
||||
float(self.product.get_actual_price()) != amount):
|
||||
raise forms.ValidationError(_("Amount field does not match"))
|
||||
return amount
|
||||
|
||||
def clean_recurring(self):
|
||||
recurring = self.cleaned_data.get('recurring')
|
||||
if (self.product.product_is_subscription !=
|
||||
(True if recurring else False)):
|
||||
raise forms.ValidationError(_("Recurring field does not match"))
|
||||
return recurring
|
||||
|
||||
|
||||
class HostingUserSignupForm(forms.ModelForm):
|
||||
confirm_password = forms.CharField(label=_("Confirm Password"),
|
||||
widget=forms.PasswordInput())
|
||||
|
|
@ -111,7 +198,7 @@ class UserHostingKeyForm(forms.ModelForm):
|
|||
public_key=openssh_pubkey_str).first().name
|
||||
KEY_EXISTS_MESSAGE = _(
|
||||
"This key exists already with the name \"%(name)s\"") % {
|
||||
'name': key_name}
|
||||
'name': key_name}
|
||||
raise forms.ValidationError(KEY_EXISTS_MESSAGE)
|
||||
|
||||
with tempfile.NamedTemporaryFile(delete=True) as tmp_public_key_file:
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-07-05 23:15+0000\n"
|
||||
"POT-Creation-Date: 2018-09-08 08:45+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
|
@ -209,7 +209,7 @@ msgstr "Du hast eine neue virtuelle Maschine bestellt!"
|
|||
|
||||
#, python-format
|
||||
msgid "Your order of <strong>%(vm_name)s</strong> has been charged."
|
||||
msgstr "Deine Bestellung von <strong>%(vm_name)s</strong> wurde erhoben."
|
||||
msgstr "Deine Bestellung von <strong>%(vm_name)s</strong> wurde entgegengenommen."
|
||||
|
||||
msgid "You can view your VM detail by clicking the button below."
|
||||
msgstr "Um die Rechnung zu sehen, klicke auf den Button unten."
|
||||
|
|
@ -222,7 +222,7 @@ msgstr "Dein Data Center Light Team"
|
|||
|
||||
#, python-format
|
||||
msgid "Your order of %(vm_name)s has been charged."
|
||||
msgstr "Deine Bestellung von %(vm_name)s wurde erhoben."
|
||||
msgstr "Deine Bestellung von %(vm_name)s wurde entgegengenommen."
|
||||
|
||||
msgid "You can view your VM detail by following the link below."
|
||||
msgstr "Um die Rechnung zu sehen, klicke auf den Link unten."
|
||||
|
|
@ -249,7 +249,7 @@ msgstr "VM Kündigung"
|
|||
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You are receiving this email because your virutal machine <strong>"
|
||||
"You are receiving this email because your virtual machine <strong>"
|
||||
"%(vm_name)s</strong> has been cancelled."
|
||||
msgstr ""
|
||||
"Du erhälst diese E-Mail, da deine virtuelle Maschine <strong>%(vm_name)s</"
|
||||
|
|
@ -265,7 +265,7 @@ msgstr "NEUE VM"
|
|||
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You are receiving this email because your virutal machine %(vm_name)s has "
|
||||
"You are receiving this email because your virtual machine %(vm_name)s has "
|
||||
"been cancelled."
|
||||
msgstr ""
|
||||
"Du erhälst diese E-Mail, da deine virtuelle Maschine %(vm_name)s gekündigt "
|
||||
|
|
@ -290,9 +290,8 @@ msgid ""
|
|||
"You are not making any payment yet. After placing your order, you will be "
|
||||
"taken to the Submit Payment Page."
|
||||
msgstr ""
|
||||
"Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst "
|
||||
"ausgelöst, nachdem Du die Bestellung auf der nächsten Seite bestätigt "
|
||||
"hast."
|
||||
"Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst ausgelöst, "
|
||||
"nachdem Du die Bestellung auf der nächsten Seite bestätigt hast."
|
||||
|
||||
msgid "SUBMIT"
|
||||
msgstr "ABSENDEN"
|
||||
|
|
@ -469,9 +468,9 @@ msgid ""
|
|||
"database."
|
||||
msgstr ""
|
||||
"Bitte wähle eine der zuvor genutzten Kreditkarten oder gib Deine "
|
||||
"Kreditkartendetails unten an. Die Bezahlung wird über "
|
||||
"<a href=\"https://stripe.com\" target=\"_blank\">Stripe</a> abgewickelt. "
|
||||
"Wir speichern Deine Kreditkartendetails nicht in unserer Datenbank."
|
||||
"Kreditkartendetails unten an. Die Bezahlung wird über <a href=\"https://"
|
||||
"stripe.com\" target=\"_blank\">Stripe</a> abgewickelt. Wir speichern Deine "
|
||||
"Kreditkartendetails nicht in unserer Datenbank."
|
||||
|
||||
msgid ""
|
||||
"Please fill in your credit card information below. We are using <a href="
|
||||
|
|
@ -631,6 +630,12 @@ msgstr ""
|
|||
"Bitte entschuldige, es scheint ein unerwarteter Fehler aufgetreten zu sein. "
|
||||
"Versuche es doch bitte noch einmal."
|
||||
|
||||
msgid "Attention:"
|
||||
msgstr "Achtung:"
|
||||
|
||||
msgid "terminating VM can not be reverted."
|
||||
msgstr "Das Beenden kann nicht rückgängig gemacht werden."
|
||||
|
||||
msgid "Something doesn't work?"
|
||||
msgstr "Etwas funktioniert nicht?"
|
||||
|
||||
|
|
@ -643,8 +648,12 @@ msgstr "KONTAKT"
|
|||
msgid "Terminate your Virtual Machine"
|
||||
msgstr "Deine Virtuelle Maschine beenden"
|
||||
|
||||
msgid "Do you want to cancel your Virtual Machine"
|
||||
msgstr "Bist Du sicher, dass Du Deine virtuelle Maschine beenden willst"
|
||||
msgid ""
|
||||
"Terminated VMs can not be revived and will not be refunded. Do you want to "
|
||||
"terminate your VM?"
|
||||
msgstr ""
|
||||
"Beendete VMs können nicht wiederhergestellt oder erstattet werden. Möchtest "
|
||||
"du die VM beenden?"
|
||||
|
||||
#, python-format
|
||||
msgid ""
|
||||
|
|
@ -723,8 +732,8 @@ msgstr "Es scheint, als hättest du diese Karte bereits hinzugefügt"
|
|||
|
||||
#, python-brace-format
|
||||
msgid "An error occurred while associating the card. Details: {details}"
|
||||
msgstr "Beim Verbinden der Karte ist ein Fehler aufgetreten. Details: "
|
||||
"{details}"
|
||||
msgstr ""
|
||||
"Beim Verbinden der Karte ist ein Fehler aufgetreten. Details: {details}"
|
||||
|
||||
msgid "Successfully associated the card with your account"
|
||||
msgstr "Die Karte wurde erfolgreich mit deinem Konto verbunden"
|
||||
|
|
@ -798,6 +807,11 @@ msgstr ""
|
|||
msgid "Error terminating VM"
|
||||
msgstr "Fehler beenden VM"
|
||||
|
||||
msgid ""
|
||||
"VM terminate action timed out. Please contact support@datacenterlight.ch for "
|
||||
"further information."
|
||||
msgstr ""
|
||||
|
||||
#, python-format
|
||||
msgid "Virtual Machine %(vm_name)s Cancelled"
|
||||
msgstr "Virtuelle Maschine %(vm_name)s Kündigung"
|
||||
|
|
@ -807,6 +821,9 @@ msgstr ""
|
|||
"Es gab einen Fehler bei der Bearbeitung Deine Anfrage. Bitte versuche es "
|
||||
"noch einmal."
|
||||
|
||||
#~ msgid "Do you want to cancel your Virtual Machine"
|
||||
#~ msgstr "Bist Du sicher, dass Du Deine virtuelle Maschine beenden willst"
|
||||
|
||||
#~ msgid "Reset your password"
|
||||
#~ msgstr "Passwort zurücksetzen"
|
||||
|
||||
|
|
|
|||
41
hosting/migrations/0048_auto_20181003_0757.py
Normal file
41
hosting/migrations/0048_auto_20181003_0757.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.4 on 2018-10-03 07:57
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import utils.mixins
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('hosting', '0047_auto_20180821_1240'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='GenericProduct',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('product_name', models.CharField(default='', max_length=128)),
|
||||
('product_slug', models.SlugField(help_text='An optional html id for the Section. Required to set as target of a link on page', unique=True)),
|
||||
('product_description', models.CharField(default='', max_length=500)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('product_price', models.DecimalField(decimal_places=2, max_digits=6)),
|
||||
('product_vat', models.DecimalField(decimal_places=4, default=0, max_digits=6)),
|
||||
('product_is_subscription', models.BooleanField(default=True)),
|
||||
],
|
||||
bases=(utils.mixins.AssignPermissionsMixin, models.Model),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='hostingorder',
|
||||
name='generic_payment_description',
|
||||
field=models.CharField(max_length=500, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='hostingorder',
|
||||
name='generic_product',
|
||||
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='hosting.GenericProduct'),
|
||||
),
|
||||
]
|
||||
20
hosting/migrations/0049_auto_20181005_0736.py
Normal file
20
hosting/migrations/0049_auto_20181005_0736.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.4 on 2018-10-05 07:36
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('hosting', '0048_auto_20181003_0757'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='genericproduct',
|
||||
name='product_slug',
|
||||
field=models.SlugField(help_text='An mandatory unique slug for the product', unique=True),
|
||||
),
|
||||
]
|
||||
|
|
@ -9,8 +9,8 @@ from django.utils.functional import cached_property
|
|||
|
||||
from datacenterlight.models import VMPricing, VMTemplate
|
||||
from membership.models import StripeCustomer, CustomUser
|
||||
from utils.models import BillingAddress
|
||||
from utils.mixins import AssignPermissionsMixin
|
||||
from utils.models import BillingAddress
|
||||
from utils.stripe_utils import StripeUtils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -61,6 +61,30 @@ class OrderDetail(AssignPermissionsMixin, models.Model):
|
|||
)
|
||||
|
||||
|
||||
class GenericProduct(AssignPermissionsMixin, models.Model):
|
||||
permissions = ('view_genericproduct',)
|
||||
product_name = models.CharField(max_length=128, default="")
|
||||
product_slug = models.SlugField(
|
||||
unique=True,
|
||||
help_text=(
|
||||
'An mandatory unique slug for the product'
|
||||
)
|
||||
)
|
||||
product_description = models.CharField(max_length=500, default="")
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
product_price = models.DecimalField(max_digits=6, decimal_places=2)
|
||||
product_vat = models.DecimalField(max_digits=6, decimal_places=4, default=0)
|
||||
product_is_subscription = models.BooleanField(default=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.product_name
|
||||
|
||||
def get_actual_price(self):
|
||||
return round(
|
||||
self.product_price + (self.product_price * self.product_vat), 2
|
||||
)
|
||||
|
||||
|
||||
class HostingOrder(AssignPermissionsMixin, models.Model):
|
||||
ORDER_APPROVED_STATUS = 'Approved'
|
||||
ORDER_DECLINED_STATUS = 'Declined'
|
||||
|
|
@ -80,7 +104,13 @@ class HostingOrder(AssignPermissionsMixin, models.Model):
|
|||
OrderDetail, null=True, blank=True, default=None,
|
||||
on_delete=models.SET_NULL
|
||||
)
|
||||
|
||||
generic_product = models.ForeignKey(
|
||||
GenericProduct, null=True, blank=True, default=None,
|
||||
on_delete=models.SET_NULL
|
||||
)
|
||||
generic_payment_description = models.CharField(
|
||||
max_length=500, null=True
|
||||
)
|
||||
permissions = ('view_hostingorder',)
|
||||
|
||||
class Meta:
|
||||
|
|
@ -89,11 +119,18 @@ class HostingOrder(AssignPermissionsMixin, models.Model):
|
|||
)
|
||||
|
||||
def __str__(self):
|
||||
return ("Order Nr: #{} - VM_ID: {} - {} - {} - "
|
||||
"Specs: {} - Price: {}").format(
|
||||
hosting_order_str = ("Order Nr: #{} - VM_ID: {} - {} - {} - "
|
||||
"Specs: {} - Price: {}").format(
|
||||
self.id, self.vm_id, self.customer.user.email, self.created_at,
|
||||
self.order_detail, self.price
|
||||
)
|
||||
if self.generic_product_id is not None:
|
||||
hosting_order_str += " - Generic Payment"
|
||||
if self.stripe_charge_id is not None:
|
||||
hosting_order_str += " - One time charge"
|
||||
else:
|
||||
hosting_order_str += " - Recurring"
|
||||
return hosting_order_str
|
||||
|
||||
@cached_property
|
||||
def status(self):
|
||||
|
|
|
|||
|
|
@ -146,9 +146,13 @@
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.vm-vmid-with-warning {
|
||||
padding: 50px 0 33px !important;
|
||||
}
|
||||
|
||||
.vm-vmid .alert {
|
||||
margin-top: 15px;
|
||||
margin-bottom: -60px;
|
||||
margin-bottom: -25px;
|
||||
}
|
||||
|
||||
.vm-item-lg {
|
||||
|
|
@ -183,6 +187,13 @@
|
|||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.vm-terminate-warning {
|
||||
letter-spacing: 0.6px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #373636;
|
||||
}
|
||||
|
||||
.vm-contact-us {
|
||||
margin: 25px 0 30px;
|
||||
/* text-align: center; */
|
||||
|
|
|
|||
|
|
@ -157,6 +157,10 @@ $( document ).ready(function() {
|
|||
/* ---------------------------------------------
|
||||
Scripts initialization
|
||||
--------------------------------------------- */
|
||||
var minRam = 1;
|
||||
if(window.minRam){
|
||||
minRam = window.minRam;
|
||||
}
|
||||
var cardPricing = {
|
||||
'cpu': {
|
||||
'id': 'coreValue',
|
||||
|
|
@ -168,7 +172,7 @@ $( document ).ready(function() {
|
|||
'ram': {
|
||||
'id': 'ramValue',
|
||||
'value': 2,
|
||||
'min': 1,
|
||||
'min': minRam,
|
||||
'max': 200,
|
||||
'interval': 1
|
||||
},
|
||||
|
|
@ -188,21 +192,54 @@ $( document ).ready(function() {
|
|||
var data = $(this).data('minus');
|
||||
|
||||
if (cardPricing[data].value > cardPricing[data].min) {
|
||||
cardPricing[data].value = Number(cardPricing[data].value) - cardPricing[data].interval;
|
||||
if(data === 'ram' && String(cardPricing[data].value) === "1" && minRam === 0.5){
|
||||
cardPricing[data].value = 0.5;
|
||||
$('#ramValue').val('0.5');
|
||||
$("#ramValue").attr('step', 0.5);
|
||||
} else {
|
||||
cardPricing[data].value = Number(cardPricing[data].value) - cardPricing[data].interval;
|
||||
}
|
||||
}
|
||||
_fetchPricing();
|
||||
$('#ramValue').data('old-value', $('#ramValue').val());
|
||||
});
|
||||
$('.fa-plus-circle.right').click(function(event) {
|
||||
var data = $(this).data('plus');
|
||||
if (cardPricing[data].value < cardPricing[data].max) {
|
||||
cardPricing[data].value = Number(cardPricing[data].value) + cardPricing[data].interval;
|
||||
if(data === 'ram' && String(cardPricing[data].value) === "0.5" && minRam === 0.5){
|
||||
cardPricing[data].value = 1;
|
||||
$('#ramValue').val('1');
|
||||
$("#ramValue").attr('step', 1);
|
||||
} else {
|
||||
cardPricing[data].value = Number(cardPricing[data].value) + cardPricing[data].interval;
|
||||
}
|
||||
}
|
||||
_fetchPricing();
|
||||
$('#ramValue').data('old-value', $('#ramValue').val());
|
||||
});
|
||||
|
||||
$('.input-price').change(function() {
|
||||
var data = $(this).attr("name");
|
||||
cardPricing[data].value = $('input[name=' + data + ']').val();
|
||||
var input = $('input[name=' + data + ']');
|
||||
var inputValue = input.val();
|
||||
|
||||
if(data === 'ram') {
|
||||
var ramInput = $('#ramValue');
|
||||
if ($('#ramValue').data('old-value') < $('#ramValue').val()) {
|
||||
if($('#ramValue').val() === '1' && minRam === 0.5) {
|
||||
$("#ramValue").attr('step', 1);
|
||||
$('#ramValue').val('1');
|
||||
}
|
||||
} else {
|
||||
if($('#ramValue').val() === '0' && minRam === 0.5) {
|
||||
$("#ramValue").attr('step', 0.5);
|
||||
$('#ramValue').val('0.5');
|
||||
}
|
||||
}
|
||||
inputValue = $('#ramValue').val();
|
||||
$('#ramValue').data('old-value', $('#ramValue').val());
|
||||
}
|
||||
cardPricing[data].value = inputValue;
|
||||
_fetchPricing();
|
||||
});
|
||||
}
|
||||
|
|
@ -236,4 +273,5 @@ $( document ).ready(function() {
|
|||
}
|
||||
|
||||
_initPricing();
|
||||
});
|
||||
$('#ramValue').data('old-value', $('#ramValue').val());
|
||||
});
|
||||
|
|
|
|||
|
|
@ -22,6 +22,39 @@ function setBrandIcon(brand) {
|
|||
|
||||
|
||||
$(document).ready(function () {
|
||||
$(function () {
|
||||
$("select#id_generic_payment_form-product_name").change(function () {
|
||||
var gp_form = $('#generic-payment-form');
|
||||
$.ajax({
|
||||
url: gp_form.attr('action'),
|
||||
type: 'POST',
|
||||
data: gp_form.serialize(),
|
||||
init: function () {
|
||||
console.log("init")
|
||||
},
|
||||
success: function (data) {
|
||||
if (data.amount !== undefined) {
|
||||
$("#id_generic_payment_form-amount").val(data.amount);
|
||||
if (data.isSubscription) {
|
||||
$('#id_generic_payment_form-recurring').prop('checked', true);
|
||||
} else {
|
||||
$('#id_generic_payment_form-recurring').prop('checked', false);
|
||||
}
|
||||
} else {
|
||||
$("#id_generic_payment_form-amount").val('');
|
||||
$('#id_generic_payment_form-recurring').prop('checked', false);
|
||||
console.log("No product found")
|
||||
}
|
||||
},
|
||||
error: function (xmlhttprequest, textstatus, message) {
|
||||
$("#id_generic_payment_form-amount").val('');
|
||||
$('#id_generic_payment_form-recurring').prop('checked', false);
|
||||
console.log("Error fetching product")
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
$.ajaxSetup({
|
||||
beforeSend: function (xhr, settings) {
|
||||
function getCookie(name) {
|
||||
|
|
@ -124,17 +157,35 @@ $(document).ready(function () {
|
|||
$('#billing-form').submit();
|
||||
}
|
||||
|
||||
function getCookie(name) {
|
||||
var value = "; " + document.cookie;
|
||||
var parts = value.split("; " + name + "=");
|
||||
if (parts.length === 2) return parts.pop().split(";").shift();
|
||||
}
|
||||
|
||||
function submitBillingForm() {
|
||||
var billing_form = $('#billing-form');
|
||||
var recurring_input = $('#id_generic_payment_form-recurring');
|
||||
billing_form.append('<input type="hidden" name="generic_payment_form-product_name" value="' + $('#id_generic_payment_form-product_name').val() + '" />');
|
||||
billing_form.append('<input type="hidden" name="generic_payment_form-amount" value="' + $('#id_generic_payment_form-amount').val() + '" />');
|
||||
if (recurring_input.attr('type') === 'hidden') {
|
||||
billing_form.append('<input type="hidden" name="generic_payment_form-recurring" value="' + (recurring_input.val() === 'True' ? 'on' : '') + '" />');
|
||||
} else {
|
||||
billing_form.append('<input type="hidden" name="generic_payment_form-recurring" value="' + (recurring_input.prop('checked') ? 'on' : '') + '" />');
|
||||
}
|
||||
billing_form.append('<input type="hidden" name="generic_payment_form-description" value="' + $('#id_generic_payment_form-description').val() + '" />');
|
||||
billing_form.submit();
|
||||
}
|
||||
|
||||
var $form_new = $('#payment-form-new');
|
||||
$form_new.submit(payWithStripe_new);
|
||||
|
||||
function payWithStripe_new(e) {
|
||||
e.preventDefault();
|
||||
|
||||
function stripeTokenHandler(token) {
|
||||
// Insert the token ID into the form so it gets submitted to the server
|
||||
$('#id_token').val(token.id);
|
||||
$('#billing-form').submit();
|
||||
submitBillingForm();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -196,10 +247,10 @@ $(document).ready(function () {
|
|||
}
|
||||
});
|
||||
|
||||
$('.credit-card-info .btn.choice-btn').click(function(){
|
||||
var id = this.dataset['id_card'];
|
||||
$('#id_card').val(id);
|
||||
$('#billing-form').submit();
|
||||
$('.credit-card-info .btn.choice-btn').click(function () {
|
||||
var id = this.dataset['id_card'];
|
||||
$('#id_card').val(id);
|
||||
submitBillingForm();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -134,3 +134,15 @@ $(document).ready(function() {
|
|||
$(this).find('.modal-footer .btn').addClass('hide');
|
||||
})
|
||||
});
|
||||
|
||||
window.onload = function () {
|
||||
var locale_dates = document.getElementsByClassName("locale_date");
|
||||
var formats = ['YYYY-MM-DD hh:mm a'];
|
||||
var i;
|
||||
for (i = 0; i < locale_dates.length; i++) {
|
||||
var oldDate = moment.utc(locale_dates[i].textContent, formats);
|
||||
var outputFormat = locale_dates[i].getAttribute('data-format') || oldDate._f;
|
||||
locale_dates[i].innerHTML = oldDate.local().format(outputFormat);
|
||||
locale_dates[i].className += ' done';
|
||||
}
|
||||
};
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
<tr>
|
||||
<td style="padding-top: 25px; font-size: 16px;">
|
||||
<p style="line-height: 1.75; font-family: Lato, Arial, sans-serif; font-weight: 300; margin: 0;">
|
||||
{% blocktrans %}You are receiving this email because your virutal machine <strong>{{ vm_name }}</strong> has been cancelled.{% endblocktrans %}
|
||||
{% blocktrans %}You are receiving this email because your virtual machine <strong>{{ vm_name }}</strong> has been cancelled.{% endblocktrans %}
|
||||
</p>
|
||||
<p style="line-height: 1.75; font-family: Lato, Arial, sans-serif; font-weight: 300; margin: 0;">
|
||||
{% blocktrans %}You can always order a new VM by clicking the button below.{% endblocktrans %}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
{% trans "Virtual Machine Cancellation" %}
|
||||
|
||||
{% blocktrans %}You are receiving this email because your virutal machine {{vm_name}} has been cancelled.{% endblocktrans %}
|
||||
{% blocktrans %}You are receiving this email because your virtual machine {{vm_name}} has been cancelled.{% endblocktrans %}
|
||||
{% blocktrans %}You can always order a new VM by following the link below.{% endblocktrans %}
|
||||
|
||||
{{ base_url }}{% url 'hosting:create_virtual_machine' %}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
{% endif %}
|
||||
</span>
|
||||
</p>
|
||||
{% if order %}
|
||||
{% if order and vm %}
|
||||
<p>
|
||||
<strong>{% trans "Status" %}: </strong>
|
||||
<strong>
|
||||
|
|
@ -93,77 +93,104 @@
|
|||
<hr>
|
||||
<div>
|
||||
<h4>{% trans "Order summary" %}</h4>
|
||||
<p>
|
||||
<strong>{% trans "Product" %}:</strong>
|
||||
{% if vm.name %}
|
||||
{{ vm.name }}
|
||||
{% else %}
|
||||
{{ request.session.template.name }}
|
||||
{% endif %}
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
{% if vm.created_at %}
|
||||
<p>
|
||||
<span>{% trans "Period" %}: </span>
|
||||
<span>
|
||||
<span class="locale_date" data-format="YYYY/MM/DD">{{ vm.created_at|date:'Y-m-d h:i a' }}</span> - <span class="locale_date" data-format="YYYY/MM/DD">{{ subscription_end_date|date:'Y-m-d h:i a' }}</span>
|
||||
</span>
|
||||
</p>
|
||||
{% if vm %}
|
||||
<p>
|
||||
<strong>{% trans "Product" %}:</strong>
|
||||
{% if vm.name %}
|
||||
{{ vm.name }}
|
||||
{% else %}
|
||||
{{ request.session.template.name }}
|
||||
{% endif %}
|
||||
<p>
|
||||
<span>{% trans "Cores" %}: </span>
|
||||
{% if vm.cores %}
|
||||
<strong class="pull-right">{{vm.cores|floatformat}}</strong>
|
||||
{% else %}
|
||||
<strong class="pull-right">{{vm.cpu|floatformat}}</strong>
|
||||
{% endif %}
|
||||
</p>
|
||||
<p>
|
||||
<span>{% trans "Memory" %}: </span>
|
||||
<strong class="pull-right">{{vm.memory}} GB</strong>
|
||||
</p>
|
||||
<p>
|
||||
<span>{% trans "Disk space" %}: </span>
|
||||
<strong class="pull-right">{{vm.disk_size}} GB</strong>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<hr class="thin-hr">
|
||||
</div>
|
||||
{% if vm.vat > 0 or vm.discount.amount > 0 %}
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="subtotal-price">
|
||||
{% if vm.vat > 0 %}
|
||||
<p>
|
||||
<strong>{% trans "Subtotal" %} </strong>
|
||||
<strong class="pull-right">{{vm.price|floatformat:2|intcomma}} CHF</strong>
|
||||
</p>
|
||||
<p>
|
||||
<small>{% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%) </small>
|
||||
<strong class="pull-right">{{vm.vat|floatformat:2|intcomma}} CHF</strong>
|
||||
</p>
|
||||
{% if vm.created_at %}
|
||||
<p>
|
||||
<span>{% trans "Period" %}: </span>
|
||||
<span>
|
||||
<span class="locale_date" data-format="YYYY/MM/DD">{{ vm.created_at|date:'Y-m-d h:i a' }}</span> - <span class="locale_date" data-format="YYYY/MM/DD">{{ subscription_end_date|date:'Y-m-d h:i a' }}</span>
|
||||
</span>
|
||||
</p>
|
||||
{% endif %}
|
||||
<p>
|
||||
<span>{% trans "Cores" %}: </span>
|
||||
{% if vm.cores %}
|
||||
<strong class="pull-right">{{vm.cores|floatformat}}</strong>
|
||||
{% else %}
|
||||
<strong class="pull-right">{{vm.cpu|floatformat}}</strong>
|
||||
{% endif %}
|
||||
{% if vm.discount.amount > 0 %}
|
||||
<p class="text-primary">
|
||||
{%trans "Discount" as discount_name %}
|
||||
<strong>{{ vm.discount.name|default:discount_name }} </strong>
|
||||
<strong class="pull-right">- {{ vm.discount.amount }} CHF</strong>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</p>
|
||||
<p>
|
||||
<span>{% trans "Memory" %}: </span>
|
||||
<strong class="pull-right">{{vm.memory}} GB</strong>
|
||||
</p>
|
||||
<p>
|
||||
<span>{% trans "Disk space" %}: </span>
|
||||
<strong class="pull-right">{{vm.disk_size}} GB</strong>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<hr class="thin-hr">
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="col-sm-6">
|
||||
<p class="total-price">
|
||||
<strong>{% trans "Total" %} </strong>
|
||||
<strong class="pull-right">{% if vm.total_price %}{{vm.total_price|floatformat:2|intcomma}}{% else %}{{vm.price|floatformat:2|intcomma}}{% endif %} CHF</strong>
|
||||
</p>
|
||||
{% if vm.vat > 0 or vm.discount.amount > 0 %}
|
||||
<div class="col-sm-6">
|
||||
<div class="subtotal-price">
|
||||
{% if vm.vat > 0 %}
|
||||
<p>
|
||||
<strong>{% trans "Subtotal" %} </strong>
|
||||
<strong class="pull-right">{{vm.price|floatformat:2|intcomma}} CHF</strong>
|
||||
</p>
|
||||
<p>
|
||||
<small>{% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%) </small>
|
||||
<strong class="pull-right">{{vm.vat|floatformat:2|intcomma}} CHF</strong>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if vm.discount.amount > 0 %}
|
||||
<p class="text-primary">
|
||||
{%trans "Discount" as discount_name %}
|
||||
<strong>{{ vm.discount.name|default:discount_name }} </strong>
|
||||
<strong class="pull-right">- {{ vm.discount.amount }} CHF</strong>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<hr class="thin-hr">
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="col-sm-6">
|
||||
<p class="total-price">
|
||||
<strong>{% trans "Total" %} </strong>
|
||||
<strong class="pull-right">{% if vm.total_price %}{{vm.total_price|floatformat:2|intcomma}}{% else %}{{vm.price|floatformat:2|intcomma}}{% endif %} CHF</strong>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>
|
||||
<strong>{% trans "Product" %}:</strong>
|
||||
{{ product_name }}
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<p>
|
||||
<span>{% trans "Amount" %}: </span>
|
||||
<strong class="pull-right">{{order.price|floatformat:2|intcomma}} CHF</strong>
|
||||
</p>
|
||||
{% if order.generic_payment_description %}
|
||||
<p>
|
||||
<span>{% trans "Description" %}: </span>
|
||||
<strong class="pull-right">{{order.generic_payment_description}}</strong>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if order.subscription_id %}
|
||||
<p>
|
||||
<span>{% trans "Recurring" %}: </span>
|
||||
<strong class="pull-right">{{order.created_at|date:'d'|ordinal}} {% trans "of every month" %}</strong>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<hr class="thin-hr">
|
||||
</div>
|
||||
|
|
@ -229,17 +256,6 @@
|
|||
<script type="text/javascript">
|
||||
{% trans "Some problem encountered. Please try again later." as err_msg %}
|
||||
var create_vm_error_message = '{{err_msg|safe}}';
|
||||
window.onload = function () {
|
||||
var locale_dates = document.getElementsByClassName("locale_date");
|
||||
var formats = ['YYYY-MM-DD hh:mm a']
|
||||
var i;
|
||||
for (i = 0; i < locale_dates.length; i++) {
|
||||
var oldDate = moment.utc(locale_dates[i].textContent, formats);
|
||||
var outputFormat = locale_dates[i].getAttribute('data-format') || oldDate._f;
|
||||
locale_dates[i].innerHTML = oldDate.local().format(outputFormat);
|
||||
locale_dates[i].className += ' done';
|
||||
}
|
||||
};
|
||||
</script>
|
||||
{%endblock%}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
{% for order in orders %}
|
||||
<tr>
|
||||
<td class="xs-td-inline" data-header="{% trans 'Order Nr.' %}">{{ order.id }}</td>
|
||||
<td class="xs-td-bighalf" data-header="{% trans 'Date' %}">{{ order.created_at | date:"M d, Y H:i" }}</td>
|
||||
<td class="xs-td-bighalf locale_date" data-header="{% trans 'Date' %}">{{ order.created_at | date:'Y-m-d h:i a' }}</td>
|
||||
<td class="xs-td-smallhalf" data-header="{% trans 'Amount' %}">{{ order.price|floatformat:2|intcomma }}</td>
|
||||
<td class="text-right last-td">
|
||||
<a class="btn btn-order-detail" href="{% url 'hosting:orders' order.pk %}">{% trans 'See Invoice' %}</a>
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@
|
|||
<h3>{%trans "Billing Address" %}</h3>
|
||||
<hr>
|
||||
<form role="form" id="billing-form" method="post" action="" novalidate>
|
||||
{% csrf_token %}
|
||||
{% for field in form %}
|
||||
{% csrf_token %}
|
||||
{% bootstrap_field field show_label=False type='fields' bound_css_class='' %}
|
||||
{% endfor %}
|
||||
<div class="form-group text-right">
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@
|
|||
</div>
|
||||
<div class="vm-detail-item">
|
||||
<h2 class="vm-detail-title">{% trans "Status" %} <img src="{% static 'hosting/img/connected.svg' %}" class="un-icon"></h2>
|
||||
<div class="vm-vmid">
|
||||
<div class="vm-vmid vm-vmid-with-warning">
|
||||
<div class="vm-item-subtitle">{% trans "Your VM is" %}</div>
|
||||
<div id="terminate-VM" data-alt="{% trans 'Terminating' %}">
|
||||
{% if virtual_machine.state == 'PENDING' %}
|
||||
|
|
@ -74,6 +74,10 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="vm-terminate-warning text-center">
|
||||
<p>{% trans "Attention:" %}</p>
|
||||
<p>{% trans "terminating VM can not be reverted." %}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vm-contact-us">
|
||||
|
|
@ -105,7 +109,7 @@
|
|||
<div class="modal-icon"><i class="fa fa-ban" aria-hidden="true"></i></div>
|
||||
<h4 class="modal-title" id="ModalLabel">{% trans "Terminate your Virtual Machine" %}</h4>
|
||||
<div class="modal-text">
|
||||
<p>{% trans "Do you want to cancel your Virtual Machine" %} ?</p>
|
||||
<p>{% trans "Terminated VMs can not be revived and will not be refunded. Do you want to terminate your VM?" %}</p>
|
||||
<p><strong>{{virtual_machine.name}}</strong></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
|
|
|||
165
hosting/views.py
165
hosting/views.py
|
|
@ -32,6 +32,7 @@ from stored_messages.api import mark_read
|
|||
from stored_messages.models import Message
|
||||
from stored_messages.settings import stored_messages_settings
|
||||
|
||||
from datacenterlight.cms_models import DCLCalculatorPluginModel
|
||||
from datacenterlight.models import VMTemplate, VMPricing
|
||||
from datacenterlight.utils import create_vm, get_cms_integration
|
||||
from hosting.models import UserCardDetail
|
||||
|
|
@ -59,7 +60,8 @@ from .forms import (
|
|||
)
|
||||
from .mixins import ProcessVMSelectionMixin, HostingContextMixin
|
||||
from .models import (
|
||||
HostingOrder, HostingBill, HostingPlan, UserHostingKey, VMDetail
|
||||
HostingOrder, HostingBill, HostingPlan, UserHostingKey, VMDetail,
|
||||
GenericProduct
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -862,32 +864,20 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView):
|
|||
raise Http404
|
||||
|
||||
if obj is not None:
|
||||
# invoice for previous order
|
||||
try:
|
||||
vm_detail = VMDetail.objects.get(vm_id=obj.vm_id)
|
||||
context['vm'] = vm_detail.__dict__
|
||||
context['vm']['name'] = '{}-{}'.format(
|
||||
context['vm']['configuration'], context['vm']['vm_id'])
|
||||
price, vat, vat_percent, discount = get_vm_price_with_vat(
|
||||
cpu=context['vm']['cores'],
|
||||
ssd_size=context['vm']['disk_size'],
|
||||
memory=context['vm']['memory'],
|
||||
pricing_name=(obj.vm_pricing.name
|
||||
if obj.vm_pricing else 'default')
|
||||
)
|
||||
context['vm']['vat'] = vat
|
||||
context['vm']['price'] = price
|
||||
context['vm']['discount'] = discount
|
||||
context['vm']['vat_percent'] = vat_percent
|
||||
context['vm']['total_price'] = price + vat - discount['amount']
|
||||
context['subscription_end_date'] = vm_detail.end_date()
|
||||
except VMDetail.DoesNotExist:
|
||||
if obj.generic_product_id is not None:
|
||||
# generic payment case
|
||||
logger.debug("Generic payment case")
|
||||
context['product_name'] = GenericProduct.objects.get(
|
||||
id=obj.generic_product_id
|
||||
).product_name
|
||||
else:
|
||||
# invoice for previous order
|
||||
logger.debug("Invoice of VM order")
|
||||
try:
|
||||
manager = OpenNebulaManager(
|
||||
email=owner.email, password=owner.password
|
||||
)
|
||||
vm = manager.get_vm(obj.vm_id)
|
||||
context['vm'] = VirtualMachineSerializer(vm).data
|
||||
vm_detail = VMDetail.objects.get(vm_id=obj.vm_id)
|
||||
context['vm'] = vm_detail.__dict__
|
||||
context['vm']['name'] = '{}-{}'.format(
|
||||
context['vm']['configuration'], context['vm']['vm_id'])
|
||||
price, vat, vat_percent, discount = get_vm_price_with_vat(
|
||||
cpu=context['vm']['cores'],
|
||||
ssd_size=context['vm']['disk_size'],
|
||||
|
|
@ -899,23 +889,43 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView):
|
|||
context['vm']['price'] = price
|
||||
context['vm']['discount'] = discount
|
||||
context['vm']['vat_percent'] = vat_percent
|
||||
context['vm']['total_price'] = (
|
||||
price + vat - discount['amount']
|
||||
)
|
||||
except WrongIdError:
|
||||
messages.error(
|
||||
self.request,
|
||||
_('The VM you are looking for is unavailable at the '
|
||||
'moment. Please contact Data Center Light support.')
|
||||
)
|
||||
self.kwargs['error'] = 'WrongIdError'
|
||||
context['error'] = 'WrongIdError'
|
||||
except ConnectionRefusedError:
|
||||
messages.error(
|
||||
self.request,
|
||||
_('In order to create a VM, you need to create/upload '
|
||||
'your SSH KEY first.')
|
||||
)
|
||||
context['vm']['total_price'] = price + vat - discount['amount']
|
||||
context['subscription_end_date'] = vm_detail.end_date()
|
||||
except VMDetail.DoesNotExist:
|
||||
try:
|
||||
manager = OpenNebulaManager(
|
||||
email=owner.email, password=owner.password
|
||||
)
|
||||
vm = manager.get_vm(obj.vm_id)
|
||||
context['vm'] = VirtualMachineSerializer(vm).data
|
||||
price, vat, vat_percent, discount = get_vm_price_with_vat(
|
||||
cpu=context['vm']['cores'],
|
||||
ssd_size=context['vm']['disk_size'],
|
||||
memory=context['vm']['memory'],
|
||||
pricing_name=(obj.vm_pricing.name
|
||||
if obj.vm_pricing else 'default')
|
||||
)
|
||||
context['vm']['vat'] = vat
|
||||
context['vm']['price'] = price
|
||||
context['vm']['discount'] = discount
|
||||
context['vm']['vat_percent'] = vat_percent
|
||||
context['vm']['total_price'] = (
|
||||
price + vat - discount['amount']
|
||||
)
|
||||
except WrongIdError:
|
||||
messages.error(
|
||||
self.request,
|
||||
_('The VM you are looking for is unavailable at the '
|
||||
'moment. Please contact Data Center Light support.')
|
||||
)
|
||||
self.kwargs['error'] = 'WrongIdError'
|
||||
context['error'] = 'WrongIdError'
|
||||
except ConnectionRefusedError:
|
||||
messages.error(
|
||||
self.request,
|
||||
_('In order to create a VM, you need to create/upload '
|
||||
'your SSH KEY first.')
|
||||
)
|
||||
else:
|
||||
# new order, confirm payment
|
||||
if 'token' in self.request.session:
|
||||
|
|
@ -1032,14 +1042,20 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView):
|
|||
memory = specs.get('memory')
|
||||
disk_size = specs.get('disk_size')
|
||||
amount_to_be_charged = specs.get('total_price')
|
||||
plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu,
|
||||
memory=memory,
|
||||
disk_size=disk_size)
|
||||
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
|
||||
ram=memory,
|
||||
ssd=disk_size,
|
||||
version=1,
|
||||
app='dcl')
|
||||
plan_name = StripeUtils.get_stripe_plan_name(
|
||||
cpu=cpu,
|
||||
memory=memory,
|
||||
disk_size=disk_size,
|
||||
price=amount_to_be_charged
|
||||
)
|
||||
stripe_plan_id = StripeUtils.get_stripe_plan_id(
|
||||
cpu=cpu,
|
||||
ram=memory,
|
||||
ssd=disk_size,
|
||||
version=1,
|
||||
app='dcl',
|
||||
price=amount_to_be_charged
|
||||
)
|
||||
stripe_plan = stripe_utils.get_or_create_stripe_plan(
|
||||
amount=amount_to_be_charged,
|
||||
name=plan_name,
|
||||
|
|
@ -1183,7 +1199,29 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
|
|||
raise ValidationError(_('Invalid number of cores'))
|
||||
|
||||
def validate_memory(self, value):
|
||||
if (value > 200) or (value < 1):
|
||||
if 'pid' in self.request.POST:
|
||||
try:
|
||||
plugin = DCLCalculatorPluginModel.objects.get(
|
||||
id=self.request.POST['pid']
|
||||
)
|
||||
except DCLCalculatorPluginModel.DoesNotExist as dne:
|
||||
logger.error(
|
||||
str(dne) + " plugin_id: " + self.request.POST['pid']
|
||||
)
|
||||
raise ValidationError(_('Invalid calculator properties'))
|
||||
if plugin.enable_512mb_ram:
|
||||
if value % 1 == 0 or value == 0.5:
|
||||
logger.debug(
|
||||
"Given ram {value} is either 0.5 or a"
|
||||
" whole number".format(value=value)
|
||||
)
|
||||
if (value > 200) or (value < 0.5):
|
||||
raise ValidationError(_('Invalid RAM size'))
|
||||
else:
|
||||
raise ValidationError(_('Invalid RAM size'))
|
||||
elif (value > 200) or (value < 1) or (value % 1 != 0):
|
||||
raise ValidationError(_('Invalid RAM size'))
|
||||
else:
|
||||
raise ValidationError(_('Invalid RAM size'))
|
||||
|
||||
def validate_storage(self, value):
|
||||
|
|
@ -1203,7 +1241,7 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
|
|||
cores = request.POST.get('cpu')
|
||||
cores_field = forms.IntegerField(validators=[self.validate_cores])
|
||||
memory = request.POST.get('ram')
|
||||
memory_field = forms.IntegerField(validators=[self.validate_memory])
|
||||
memory_field = forms.FloatField(validators=[self.validate_memory])
|
||||
storage = request.POST.get('storage')
|
||||
storage_field = forms.IntegerField(validators=[self.validate_storage])
|
||||
template_id = int(request.POST.get('config'))
|
||||
|
|
@ -1267,7 +1305,7 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
|
|||
'price': price,
|
||||
'vat': vat,
|
||||
'vat_percent': vat_percent,
|
||||
'total_price': price + vat - discount['amount'],
|
||||
'total_price': round(price + vat - discount['amount'], 2),
|
||||
'pricing_name': vm_pricing_name
|
||||
}
|
||||
|
||||
|
|
@ -1394,7 +1432,7 @@ class VirtualMachineView(LoginRequiredMixin, View):
|
|||
terminated = manager.delete_vm(vm.id)
|
||||
|
||||
if not terminated:
|
||||
logger.debug(
|
||||
logger.error(
|
||||
"manager.delete_vm returned False. Hence, error making "
|
||||
"xml-rpc call to delete vm failed."
|
||||
)
|
||||
|
|
@ -1404,6 +1442,9 @@ class VirtualMachineView(LoginRequiredMixin, View):
|
|||
try:
|
||||
manager.get_vm(vm.id)
|
||||
except WrongIdError:
|
||||
logger.error(
|
||||
"VM {} not found. So, its terminated.".format(vm.id)
|
||||
)
|
||||
response['status'] = True
|
||||
response['text'] = ugettext('Terminated')
|
||||
vm_detail_obj = VMDetail.objects.filter(
|
||||
|
|
@ -1421,6 +1462,10 @@ class VirtualMachineView(LoginRequiredMixin, View):
|
|||
break
|
||||
else:
|
||||
sleep(2)
|
||||
if not response['status']:
|
||||
response['text'] = _("VM terminate action timed out. Please "
|
||||
"contact support@datacenterlight.ch for "
|
||||
"further information.")
|
||||
context = {
|
||||
'vm_name': vm_name,
|
||||
'base_url': "{0}://{1}".format(
|
||||
|
|
@ -1441,11 +1486,13 @@ class VirtualMachineView(LoginRequiredMixin, View):
|
|||
email = BaseEmail(**email_data)
|
||||
email.send()
|
||||
admin_email_body.update(response)
|
||||
admin_msg_sub = "VM and Subscription for VM {} and user: {}".format(
|
||||
vm.id,
|
||||
owner.email
|
||||
)
|
||||
email_to_admin_data = {
|
||||
'subject': "Deleted VM and Subscription for VM {vm_id} and "
|
||||
"user: {user}".format(
|
||||
vm_id=vm.id, user=owner.email
|
||||
),
|
||||
'subject': ("Deleted " if response['status']
|
||||
else "ERROR deleting ") + admin_msg_sub,
|
||||
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
|
||||
'to': ['info@ungleich.ch'],
|
||||
'body': "\n".join(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue