merge master
This commit is contained in:
commit
275ea47e41
32 changed files with 530 additions and 125 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -42,3 +42,4 @@ secret-key
|
||||||
|
|
||||||
# to keep empty dirs
|
# to keep empty dirs
|
||||||
!.gitkeep
|
!.gitkeep
|
||||||
|
*.orig
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
next:
|
||||||
|
* bgfix: [all] Make /blog available on all domains
|
||||||
|
* #4367: [dcl] email logo resolution fix
|
||||||
|
* #4376: [cms] dcl promo section plugin link color changed to brighter shade
|
||||||
1.6.5: 2018-04-08
|
1.6.5: 2018-04-08
|
||||||
* #4396: [ungleich] add favicon to ungleich blog
|
* #4396: [ungleich] add favicon to ungleich blog
|
||||||
* #4327: [dcl] fix navbar logo repeat
|
* #4327: [dcl] fix navbar logo repeat
|
||||||
|
|
|
@ -2,6 +2,7 @@ from django.contrib import admin
|
||||||
from cms.admin.placeholderadmin import PlaceholderAdminMixin
|
from cms.admin.placeholderadmin import PlaceholderAdminMixin
|
||||||
from cms.extensions import PageExtensionAdmin
|
from cms.extensions import PageExtensionAdmin
|
||||||
from .cms_models import CMSIntegration, CMSFaviconExtension
|
from .cms_models import CMSIntegration, CMSFaviconExtension
|
||||||
|
from .models import VMPricing
|
||||||
|
|
||||||
|
|
||||||
class CMSIntegrationAdmin(PlaceholderAdminMixin, admin.ModelAdmin):
|
class CMSIntegrationAdmin(PlaceholderAdminMixin, admin.ModelAdmin):
|
||||||
|
@ -14,3 +15,4 @@ class CMSFaviconExtensionAdmin(PageExtensionAdmin):
|
||||||
|
|
||||||
admin.site.register(CMSIntegration, CMSIntegrationAdmin)
|
admin.site.register(CMSIntegration, CMSIntegrationAdmin)
|
||||||
admin.site.register(CMSFaviconExtension, CMSFaviconExtensionAdmin)
|
admin.site.register(CMSFaviconExtension, CMSFaviconExtensionAdmin)
|
||||||
|
admin.site.register(VMPricing)
|
||||||
|
|
|
@ -9,6 +9,8 @@ from djangocms_text_ckeditor.fields import HTMLField
|
||||||
from filer.fields.file import FilerFileField
|
from filer.fields.file import FilerFileField
|
||||||
from filer.fields.image import FilerImageField
|
from filer.fields.image import FilerImageField
|
||||||
|
|
||||||
|
from datacenterlight.models import VMPricing
|
||||||
|
|
||||||
|
|
||||||
class CMSIntegration(models.Model):
|
class CMSIntegration(models.Model):
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
|
@ -284,3 +286,12 @@ class DCLSectionPromoPluginModel(CMSPlugin):
|
||||||
if self.background_image:
|
if self.background_image:
|
||||||
extra_classes += ' promo-with-bg'
|
extra_classes += ' promo-with-bg'
|
||||||
return extra_classes
|
return extra_classes
|
||||||
|
|
||||||
|
|
||||||
|
class DCLCustomPricingModel(CMSPlugin):
|
||||||
|
pricing = models.ForeignKey(
|
||||||
|
VMPricing,
|
||||||
|
related_name="dcl_custom_pricing_vm_pricing",
|
||||||
|
help_text='Choose a pricing that will be associated with this '
|
||||||
|
'Calculator'
|
||||||
|
)
|
||||||
|
|
|
@ -6,9 +6,9 @@ from .cms_models import (
|
||||||
DCLFooterPluginModel, DCLLinkPluginModel, DCLNavbarDropdownPluginModel,
|
DCLFooterPluginModel, DCLLinkPluginModel, DCLNavbarDropdownPluginModel,
|
||||||
DCLSectionIconPluginModel, DCLSectionImagePluginModel,
|
DCLSectionIconPluginModel, DCLSectionImagePluginModel,
|
||||||
DCLSectionPluginModel, DCLNavbarPluginModel,
|
DCLSectionPluginModel, DCLNavbarPluginModel,
|
||||||
DCLSectionPromoPluginModel
|
DCLSectionPromoPluginModel, DCLCustomPricingModel
|
||||||
)
|
)
|
||||||
from .models import VMTemplate
|
from .models import VMTemplate, VMPricing
|
||||||
|
|
||||||
|
|
||||||
@plugin_pool.register_plugin
|
@plugin_pool.register_plugin
|
||||||
|
@ -75,13 +75,13 @@ class DCLSectionPromoPlugin(CMSPluginBase):
|
||||||
@plugin_pool.register_plugin
|
@plugin_pool.register_plugin
|
||||||
class DCLCalculatorPlugin(CMSPluginBase):
|
class DCLCalculatorPlugin(CMSPluginBase):
|
||||||
module = "Datacenterlight"
|
module = "Datacenterlight"
|
||||||
name = "DCL Calculator Plugin"
|
name = "DCL Calculator Section Plugin"
|
||||||
model = DCLSectionPluginModel
|
model = DCLSectionPluginModel
|
||||||
render_template = "datacenterlight/cms/calculator.html"
|
render_template = "datacenterlight/cms/calculator.html"
|
||||||
cache = False
|
cache = False
|
||||||
allow_children = True
|
allow_children = True
|
||||||
child_classes = [
|
child_classes = [
|
||||||
'DCLSectionPromoPlugin', 'UngleichHTMLPlugin'
|
'DCLSectionPromoPlugin', 'UngleichHTMLPlugin', 'DCLCustomPricingPlugin'
|
||||||
]
|
]
|
||||||
|
|
||||||
def render(self, context, instance, placeholder):
|
def render(self, context, instance, placeholder):
|
||||||
|
@ -89,15 +89,38 @@ class DCLCalculatorPlugin(CMSPluginBase):
|
||||||
context, instance, placeholder
|
context, instance, placeholder
|
||||||
)
|
)
|
||||||
context['templates'] = VMTemplate.objects.all()
|
context['templates'] = VMTemplate.objects.all()
|
||||||
context['children_to_side'] = []
|
|
||||||
context['children_to_content'] = []
|
context['children_to_content'] = []
|
||||||
|
pricing_plugin_model = None
|
||||||
if instance.child_plugin_instances is not None:
|
if instance.child_plugin_instances is not None:
|
||||||
context['children_to_content'].extend(
|
context['children_to_content'].extend(
|
||||||
instance.child_plugin_instances
|
instance.child_plugin_instances
|
||||||
)
|
)
|
||||||
|
for child in instance.child_plugin_instances:
|
||||||
|
if child.__class__.__name__ == 'DCLCustomPricingModel':
|
||||||
|
# The second clause is just to make sure we pick up the
|
||||||
|
# most recent CustomPricing, if more than one is present
|
||||||
|
if (pricing_plugin_model is None or child.pricing_id >
|
||||||
|
pricing_plugin_model.model.pricing_id):
|
||||||
|
pricing_plugin_model = child
|
||||||
|
|
||||||
|
if pricing_plugin_model:
|
||||||
|
context['vm_pricing'] = VMPricing.get_vm_pricing_by_name(
|
||||||
|
name=pricing_plugin_model.pricing.name
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
context['vm_pricing'] = VMPricing.get_default_pricing()
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@plugin_pool.register_plugin
|
||||||
|
class DCLCustomPricingPlugin(CMSPluginBase):
|
||||||
|
module = "Datacenterlight"
|
||||||
|
name = "DCL Custom Pricing Plugin"
|
||||||
|
model = DCLCustomPricingModel
|
||||||
|
render_plugin = False
|
||||||
|
|
||||||
|
|
||||||
@plugin_pool.register_plugin
|
@plugin_pool.register_plugin
|
||||||
class DCLBannerListPlugin(CMSPluginBase):
|
class DCLBannerListPlugin(CMSPluginBase):
|
||||||
module = "Datacenterlight"
|
module = "Datacenterlight"
|
||||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-03-30 21:29+0000\n"
|
"POT-Creation-Date: 2018-04-17 19:26+0000\n"
|
||||||
"PO-Revision-Date: 2018-03-30 23:22+0000\n"
|
"PO-Revision-Date: 2018-03-30 23:22+0000\n"
|
||||||
"Last-Translator: b'Anonymous User <coder.purple+25@gmail.com>'\n"
|
"Last-Translator: b'Anonymous User <coder.purple+25@gmail.com>'\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -72,9 +72,9 @@ msgstr "Data Center Light Account Aktivierung"
|
||||||
|
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"You can activate your Data Center Light account by clicking <a "
|
"You can activate your Data Center Light account by clicking <a href="
|
||||||
"href=\"%(base_url)s%(activation_link)s\" style=\"text-decoration: none; "
|
"\"%(base_url)s%(activation_link)s\" style=\"text-decoration: none; color: "
|
||||||
"color: #4382c8; font-weight: 400;\">here</a>."
|
"#4382c8; font-weight: 400;\">here</a>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Klicke <a href=\"%(base_url)s%(activation_link)s\"style=\"text-decoration: "
|
"Klicke <a href=\"%(base_url)s%(activation_link)s\"style=\"text-decoration: "
|
||||||
"none; color: #4382c8; font-weight: 400;\">hier</a> um deinen Data Center "
|
"none; color: #4382c8; font-weight: 400;\">hier</a> um deinen Data Center "
|
||||||
|
@ -97,12 +97,13 @@ msgstr "Deine E-Mail-Adresse"
|
||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr "Passwort"
|
msgstr "Passwort"
|
||||||
|
|
||||||
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"You can reset your password <a href=\"%(base_url)s%(reset_password_url)s\" "
|
"You can reset your password <a href=\"%(base_url)s%(reset_password_url)s\" "
|
||||||
"style=\"text-decoration: none; color: #4382c8; font-weight: 400;\">here</a>."
|
"style=\"text-decoration: none; color: #4382c8; font-weight: 400;\">here</a>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Du kannst dein Passwort <a href=\"%(base_url)s%(reset_password_url)s\" "
|
"Du kannst dein Passwort <a href=\"%(base_url)s%(reset_password_url)s\" style="
|
||||||
"style=\"text-decoration: none; color: #4382c8; font-weight: 400;\">hier</a> "
|
"\"text-decoration: none; color: #4382c8; font-weight: 400;\">hier</a> "
|
||||||
"zurücksetzen."
|
"zurücksetzen."
|
||||||
|
|
||||||
msgid "Your Data Center Light Team"
|
msgid "Your Data Center Light Team"
|
||||||
|
@ -160,21 +161,6 @@ msgstr "Weiter"
|
||||||
msgid "Home"
|
msgid "Home"
|
||||||
msgstr "Home"
|
msgstr "Home"
|
||||||
|
|
||||||
msgid "Highlights"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Scale out"
|
|
||||||
msgstr "Skalierung"
|
|
||||||
|
|
||||||
msgid "Reliable and light"
|
|
||||||
msgstr "Zuverlässig und leicht"
|
|
||||||
|
|
||||||
msgid "Pricing"
|
|
||||||
msgstr "Preise"
|
|
||||||
|
|
||||||
msgid "Order VM"
|
|
||||||
msgstr "VM bestellen"
|
|
||||||
|
|
||||||
msgid "Contact"
|
msgid "Contact"
|
||||||
msgstr "Kontakt"
|
msgstr "Kontakt"
|
||||||
|
|
||||||
|
@ -184,6 +170,9 @@ msgstr "Nutzungsbedingungen"
|
||||||
msgid "Finally, an affordable VM hosting in Switzerland!"
|
msgid "Finally, an affordable VM hosting in Switzerland!"
|
||||||
msgstr "Endlich: bezahlbares VM Hosting in der Schweiz"
|
msgstr "Endlich: bezahlbares VM Hosting in der Schweiz"
|
||||||
|
|
||||||
|
msgid "Highlights"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "I want it!"
|
msgid "I want it!"
|
||||||
msgstr "Das will ich haben!"
|
msgstr "Das will ich haben!"
|
||||||
|
|
||||||
|
@ -214,6 +203,9 @@ msgstr ""
|
||||||
"mit FOSS (Free Open Source Software) arbeitet und wir daher auf "
|
"mit FOSS (Free Open Source Software) arbeitet und wir daher auf "
|
||||||
"Lizenzgebühren verzichten können.
"
|
"Lizenzgebühren verzichten können.
"
|
||||||
|
|
||||||
|
msgid "Scale out"
|
||||||
|
msgstr "Skalierung"
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"We don't use special hardware. We use commodity hardware: we buy computers "
|
"We don't use special hardware. We use commodity hardware: we buy computers "
|
||||||
"that you buy. Just many more and put them in a cozy home for computers "
|
"that you buy. Just many more and put them in a cozy home for computers "
|
||||||
|
@ -223,6 +215,9 @@ msgstr ""
|
||||||
"erschwingliche Systeme. Bei grösserer Auslastung werden mehr "
|
"erschwingliche Systeme. Bei grösserer Auslastung werden mehr "
|
||||||
"Standardkomponenten hinzugekauft und skalieren so das Datencenter."
|
"Standardkomponenten hinzugekauft und skalieren so das Datencenter."
|
||||||
|
|
||||||
|
msgid "Reliable and light"
|
||||||
|
msgstr "Zuverlässig und leicht"
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"Our VMs are located in Switzerland, with reliable power supply and fast "
|
"Our VMs are located in Switzerland, with reliable power supply and fast "
|
||||||
"internet connection. Our VM costs less thanks to our featherlight "
|
"internet connection. Our VM costs less thanks to our featherlight "
|
||||||
|
@ -232,8 +227,7 @@ msgstr ""
|
||||||
"Energieversorgung, sowie schneller Internetverbindung ausgestattet. Unser "
|
"Energieversorgung, sowie schneller Internetverbindung ausgestattet. Unser "
|
||||||
"Angebot ist aufgrund unserer leichten Infrastruktur überaus kostengünstig."
|
"Angebot ist aufgrund unserer leichten Infrastruktur überaus kostengünstig."
|
||||||
|
|
||||||
msgid ""
|
msgid "Simple and affordable: Try our virtual machine with featherlight price."
|
||||||
"Simple and affordable: Try our virtual machine with featherlight price."
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Einfach und bezahlbar: Teste nun unsere virtuellen Maschinen mit "
|
"Einfach und bezahlbar: Teste nun unsere virtuellen Maschinen mit "
|
||||||
"federleichten Preisen."
|
"federleichten Preisen."
|
||||||
|
@ -314,6 +308,9 @@ msgstr "Gesamt"
|
||||||
msgid "including VAT"
|
msgid "including VAT"
|
||||||
msgstr "inkl. Mehrwertsteuer"
|
msgstr "inkl. Mehrwertsteuer"
|
||||||
|
|
||||||
|
msgid "excluding VAT"
|
||||||
|
msgstr "exkl. Mehrwertsteuer"
|
||||||
|
|
||||||
msgid "Month"
|
msgid "Month"
|
||||||
msgstr "Monat"
|
msgstr "Monat"
|
||||||
|
|
||||||
|
@ -321,13 +318,13 @@ msgid "Credit Card"
|
||||||
msgstr "Kreditkarte"
|
msgstr "Kreditkarte"
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"Please fill in your credit card information below. We are using <a "
|
"Please fill in your credit card information below. We are using <a href="
|
||||||
"href=\"https://stripe.com\" target=\"_blank\">Stripe</a> for payment and do "
|
"\"https://stripe.com\" target=\"_blank\">Stripe</a> for payment and do not "
|
||||||
"not store your information in our database."
|
"store your information in our database."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Bitte fülle Deine Kreditkarteninformationen unten aus. Wir nutzen <a "
|
"Bitte fülle Deine Kreditkarteninformationen unten aus. Wir nutzen <a href="
|
||||||
"href=\"https://stripe.com\" target=\"_blank\">Stripe</a> für die Bezahlung "
|
"\"https://stripe.com\" target=\"_blank\">Stripe</a> für die Bezahlung und "
|
||||||
"und speichern keine Informationen in unserer Datenbank."
|
"speichern keine Informationen in unserer Datenbank."
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"You are not making any payment yet. After submitting your card information, "
|
"You are not making any payment yet. After submitting your card information, "
|
||||||
|
@ -383,13 +380,18 @@ msgstr "Bestellungsübersicht"
|
||||||
msgid "Product"
|
msgid "Product"
|
||||||
msgstr "Produkt"
|
msgstr "Produkt"
|
||||||
|
|
||||||
#, python-format
|
msgid "Subtotal"
|
||||||
|
msgstr "Zwischensumme"
|
||||||
|
|
||||||
|
msgid "VAT"
|
||||||
|
msgstr "Mehrwertsteuer"
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"By clicking \"Place order\" this plan will charge your credit card account "
|
"By clicking \"Place order\" this plan will charge your credit card account "
|
||||||
"with the fee of %(vm_price)sCHF/month"
|
"with the fee of %(vm_total_price)s CHF/month"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(vm_price)sCHF "
|
"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit "
|
||||||
"pro Monat belastet"
|
"%(vm_total_price)s CHF pro Monat belastet"
|
||||||
|
|
||||||
msgid "Place order"
|
msgid "Place order"
|
||||||
msgstr "Bestellen"
|
msgstr "Bestellen"
|
||||||
|
@ -459,21 +461,21 @@ msgid ""
|
||||||
"community."
|
"community."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Data Center Light ist ein Teil der Free und Opens Source Software (FOSS) "
|
"Data Center Light ist ein Teil der Free und Opens Source Software (FOSS) "
|
||||||
"Bewegung.<br/> Wir sind damit gross geworden, leben damit und glauben "
|
"Bewegung.<br/> Wir sind damit gross geworden, leben damit und glauben daran."
|
||||||
"daran.<br/> Je weiter wir mit unserem Data Center Light vorankommen, desto "
|
"<br/> Je weiter wir mit unserem Data Center Light vorankommen, desto mehr "
|
||||||
"mehr können wir etwas an die FOSS Community zurückgeben."
|
"können wir etwas an die FOSS Community zurückgeben."
|
||||||
|
|
||||||
msgid "We bring the future to you."
|
msgid "We bring the future to you."
|
||||||
msgstr "Wir bringen die Zukunft zu dir."
|
msgstr "Wir bringen die Zukunft zu dir."
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"Data Center Light uses the most modern technologies out there.<br>Your VM "
|
"Data Center Light uses the most modern technologies out there.<br>Your VM "
|
||||||
"needs only IPv6. Data Center Light provides<br> transparent two-way "
|
"needs only IPv6. Data Center Light provides<br> transparent two-way IPv6/"
|
||||||
"IPv6/IPv4 translation."
|
"IPv4 translation."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Data Center Light verwendet die zur Zeit modernsten Technologien.<br/>Deine "
|
"Data Center Light verwendet die zur Zeit modernsten Technologien.<br/>Deine "
|
||||||
"VM läuft mit IPv6. Data Center Light bietet eine transparente "
|
"VM läuft mit IPv6. Data Center Light bietet eine transparente IPv6/IPv4-"
|
||||||
"IPv6/IPv4-Zweiweglösung."
|
"Zweiweglösung."
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
" No more spinning metal plates! Data Center Light uses only SSDs. We keep "
|
" No more spinning metal plates! Data Center Light uses only SSDs. We keep "
|
||||||
|
@ -497,6 +499,10 @@ msgstr "Ungültige RAM-Grösse"
|
||||||
msgid "Invalid storage size"
|
msgid "Invalid storage size"
|
||||||
msgstr "Ungültige Speicher-Grösse"
|
msgstr "Ungültige Speicher-Grösse"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Incorrect pricing name. Please contact support{support_email}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Confirm Order"
|
msgid "Confirm Order"
|
||||||
msgstr "Bestellung Bestätigen"
|
msgstr "Bestellung Bestätigen"
|
||||||
|
|
||||||
|
@ -520,6 +526,12 @@ msgstr ""
|
||||||
"Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du "
|
"Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du "
|
||||||
"auf sie zugreifen kannst."
|
"auf sie zugreifen kannst."
|
||||||
|
|
||||||
|
#~ msgid "Pricing"
|
||||||
|
#~ msgstr "Preise"
|
||||||
|
|
||||||
|
#~ msgid "Order VM"
|
||||||
|
#~ msgstr "VM bestellen"
|
||||||
|
|
||||||
#~ msgid "Enter name"
|
#~ msgid "Enter name"
|
||||||
#~ msgstr "Name"
|
#~ msgstr "Name"
|
||||||
|
|
||||||
|
@ -533,18 +545,19 @@ msgstr ""
|
||||||
#~ msgstr "Anfrage verschickt"
|
#~ msgstr "Anfrage verschickt"
|
||||||
|
|
||||||
#~ msgid ""
|
#~ msgid ""
|
||||||
#~ "Thank you for your subscription! You will receive a confirmation mail from "
|
#~ "Thank you for your subscription! You will receive a confirmation mail "
|
||||||
#~ "our team"
|
#~ "from our team"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
#~ "Vielen dank für Ihre Anmeldung. Sie erhalten in kürze eine Bestätigungsmail "
|
#~ "Vielen dank für Ihre Anmeldung. Sie erhalten in kürze eine "
|
||||||
#~ "von unserem Team"
|
#~ "Bestätigungsmail von unserem Team"
|
||||||
|
|
||||||
#~ msgid "Thank you for your request."
|
#~ msgid "Thank you for your request."
|
||||||
#~ msgstr "Vielen Dank für Deine Anfrage."
|
#~ msgstr "Vielen Dank für Deine Anfrage."
|
||||||
|
|
||||||
#~ msgid "You are one step away from being our beta tester!"
|
#~ msgid "You are one step away from being our beta tester!"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
#~ "Sie sind nur noch einen Schritt davon entfernt, unser Beta-Tester zu werden!"
|
#~ "Sie sind nur noch einen Schritt davon entfernt, unser Beta-Tester zu "
|
||||||
|
#~ "werden!"
|
||||||
|
|
||||||
#~ msgid ""
|
#~ msgid ""
|
||||||
#~ "Currently we are running our tests to make sure everything runs perfectly."
|
#~ "Currently we are running our tests to make sure everything runs perfectly."
|
||||||
|
@ -553,8 +566,8 @@ msgstr ""
|
||||||
#~ "sicherzustellen."
|
#~ "sicherzustellen."
|
||||||
|
|
||||||
#~ msgid ""
|
#~ msgid ""
|
||||||
#~ "In the meantime, we would like to ask you a little patience<br/> until our "
|
#~ "In the meantime, we would like to ask you a little patience<br/> until "
|
||||||
#~ "team contacts you with beta access."
|
#~ "our team contacts you with beta access."
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
#~ "Wir werden dann sobald als möglich Ihren Beta-Zugang erstellen und Sie "
|
#~ "Wir werden dann sobald als möglich Ihren Beta-Zugang erstellen und Sie "
|
||||||
#~ "daraufhin kontaktieren.Bis dahin bitten wir Sie um etwas Geduld."
|
#~ "daraufhin kontaktieren.Bis dahin bitten wir Sie um etwas Geduld."
|
||||||
|
@ -564,8 +577,8 @@ msgstr ""
|
||||||
|
|
||||||
#~ msgid "Thank you for order! Our team will contact you via email"
|
#~ msgid "Thank you for order! Our team will contact you via email"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
#~ "Vielen Dank für die Bestellung. Unser Team setzt sich sobald wie möglich mit"
|
#~ "Vielen Dank für die Bestellung. Unser Team setzt sich sobald wie möglich "
|
||||||
#~ " Dir via E-Mail in Verbindung."
|
#~ "mit Dir via E-Mail in Verbindung."
|
||||||
|
|
||||||
#~ msgid "Affordable VM hosting based in Switzerland"
|
#~ msgid "Affordable VM hosting based in Switzerland"
|
||||||
#~ msgstr "Bezahlbares VM Hosting in der Schweiz"
|
#~ msgstr "Bezahlbares VM Hosting in der Schweiz"
|
||||||
|
@ -581,18 +594,18 @@ msgstr ""
|
||||||
|
|
||||||
#~ msgid ""
|
#~ msgid ""
|
||||||
#~ "Our VMs are hosted in Glarus, Switzerland, and our website is currently "
|
#~ "Our VMs are hosted in Glarus, Switzerland, and our website is currently "
|
||||||
#~ "running in BETA mode. If you want more information that you did not find on "
|
#~ "running in BETA mode. If you want more information that you did not find "
|
||||||
#~ "our website, or if your order is more detailed, or if you encounter any "
|
#~ "on our website, or if your order is more detailed, or if you encounter "
|
||||||
#~ "technical hiccups, please contact us at support@datacenterlight.ch, our team"
|
#~ "any technical hiccups, please contact us at support@datacenterlight.ch, "
|
||||||
#~ " will get in touch with you asap."
|
#~ "our team will get in touch with you asap."
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
#~ "Unsere VMs werden in der Schweiz im Kanton Glarus gehostet und befinden sich"
|
#~ "Unsere VMs werden in der Schweiz im Kanton Glarus gehostet und befinden "
|
||||||
#~ " zur Zeit noch in der BETA-Phase. Möchtest du mehr über uns erfahren und "
|
#~ "sich zur Zeit noch in der BETA-Phase. Möchtest du mehr über uns erfahren "
|
||||||
#~ "hast auf unserer Website nicht genügend Informationen gefunden? Möchtest "
|
#~ "und hast auf unserer Website nicht genügend Informationen gefunden? "
|
||||||
#~ "eine detailliertere Bestellung aufgeben? Bist du auf technische Probleme "
|
#~ "Möchtest eine detailliertere Bestellung aufgeben? Bist du auf technische "
|
||||||
#~ "gestossen, die du uns mitteilen möchtest? Dann zögere nicht und kontaktiere "
|
#~ "Probleme gestossen, die du uns mitteilen möchtest? Dann zögere nicht und "
|
||||||
#~ "uns unter support@datacenterlight.ch. Unser Team wird sich umgehend um dein "
|
#~ "kontaktiere uns unter support@datacenterlight.ch. Unser Team wird sich "
|
||||||
#~ "Anliegen kümmern!"
|
#~ "umgehend um dein Anliegen kümmern!"
|
||||||
|
|
||||||
#~ msgid "is not a proper name"
|
#~ msgid "is not a proper name"
|
||||||
#~ msgstr "ist kein gültiger Name"
|
#~ msgstr "ist kein gültiger Name"
|
||||||
|
@ -610,12 +623,14 @@ msgstr ""
|
||||||
#~ "\n"
|
#~ "\n"
|
||||||
#~ "Hi,\n"
|
#~ "Hi,\n"
|
||||||
#~ "\n"
|
#~ "\n"
|
||||||
#~ "You can activate your %(dcl_text)s account by clicking here %(base_url)s%(activation_link)s\n"
|
#~ "You can activate your %(dcl_text)s account by clicking here %(base_url)s"
|
||||||
|
#~ "%(activation_link)s\n"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
#~ "\n"
|
#~ "\n"
|
||||||
#~ "Hallo,\n"
|
#~ "Hallo,\n"
|
||||||
#~ "\n"
|
#~ "\n"
|
||||||
#~ "Du kannst deinen %(dcl_text)s Account aktivieren, indem du hier klickst %(base_url)s%(activation_link)s\n"
|
#~ "Du kannst deinen %(dcl_text)s Account aktivieren, indem du hier klickst "
|
||||||
|
#~ "%(base_url)s%(activation_link)s\n"
|
||||||
|
|
||||||
#~ msgid "Your"
|
#~ msgid "Your"
|
||||||
#~ msgstr "Dein"
|
#~ msgstr "Dein"
|
||||||
|
@ -650,12 +665,14 @@ msgstr ""
|
||||||
#~ msgid "I want to have it!"
|
#~ msgid "I want to have it!"
|
||||||
#~ msgstr "Das möchte ich haben!"
|
#~ msgstr "Das möchte ich haben!"
|
||||||
|
|
||||||
#~ msgid "Reuse existing factory halls intead of building an expensive building."
|
#~ msgid ""
|
||||||
|
#~ "Reuse existing factory halls intead of building an expensive building."
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
#~ "Nachhaltigkeit: Wiederverwendung ehemaliger Fabrikhallen an Stelle der "
|
#~ "Nachhaltigkeit: Wiederverwendung ehemaliger Fabrikhallen an Stelle der "
|
||||||
#~ "Errichtung eines neuen Gebäudes"
|
#~ "Errichtung eines neuen Gebäudes"
|
||||||
|
|
||||||
#~ msgid "Being creative, using modern and alternative design for a datacenter."
|
#~ msgid ""
|
||||||
|
#~ "Being creative, using modern and alternative design for a datacenter."
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
#~ "Kreativität: Verwendung eines modernen und alternativen Designs für unser "
|
#~ "Kreativität: Verwendung eines modernen und alternativen Designs für unser "
|
||||||
#~ "Datencenter"
|
#~ "Datencenter"
|
||||||
|
@ -678,8 +695,8 @@ msgstr ""
|
||||||
#~ msgstr "Standort des Datacenters ist in der Schweiz"
|
#~ msgstr "Standort des Datacenters ist in der Schweiz"
|
||||||
|
|
||||||
#~ msgid ""
|
#~ msgid ""
|
||||||
#~ " WARNING: We are currently running in BETA mode. We hope you won't encounter"
|
#~ " WARNING: We are currently running in BETA mode. We hope you won't "
|
||||||
#~ " any hiccups, but if you do, please let us know at "
|
#~ "encounter any hiccups, but if you do, please let us know at "
|
||||||
#~ "support@datacenterlight.ch"
|
#~ "support@datacenterlight.ch"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
#~ " Achtung: Wir befinden uns zurzeit im Beta-Release. Wir hoffen, dass Sie "
|
#~ " Achtung: Wir befinden uns zurzeit im Beta-Release. Wir hoffen, dass Sie "
|
||||||
|
@ -693,8 +710,8 @@ msgstr ""
|
||||||
#~ msgstr "Unser Versprechen"
|
#~ msgstr "Unser Versprechen"
|
||||||
|
|
||||||
#~ msgid ""
|
#~ msgid ""
|
||||||
#~ "Instead of creating an expensive SLA for availability, we promise that we do"
|
#~ "Instead of creating an expensive SLA for availability, we promise that we "
|
||||||
#~ " our best to run things as smooth as possible."
|
#~ "do our best to run things as smooth as possible."
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
#~ "Anstatt eines SLAs (Service Levle Agreements) zu vereinbaren,setzen wir "
|
#~ "Anstatt eines SLAs (Service Levle Agreements) zu vereinbaren,setzen wir "
|
||||||
#~ "unsere persönliche Arbeitskraft ein, um Ihnen ein sorgenfreiesHosting zu "
|
#~ "unsere persönliche Arbeitskraft ein, um Ihnen ein sorgenfreiesHosting zu "
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
|
from datacenterlight.models import VMPricing
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = '''Creates default VMPricing object'''
|
||||||
|
DEFAULT_VMPRICING_NAME = 'default'
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
self.create_default_vm_pricing()
|
||||||
|
|
||||||
|
def create_default_vm_pricing(self):
|
||||||
|
obj, created = VMPricing.objects.get_or_create(
|
||||||
|
name=self.DEFAULT_VMPRICING_NAME,
|
||||||
|
defaults={
|
||||||
|
"vat_inclusive": True,
|
||||||
|
"cores_unit_price": 5,
|
||||||
|
"ram_unit_price": 2,
|
||||||
|
"ssd_unit_price": 0.6,
|
||||||
|
"hdd_unit_price": 0.01
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if created:
|
||||||
|
print(
|
||||||
|
'Successfully created {} VMPricing object'.format(
|
||||||
|
self.DEFAULT_VMPRICING_NAME
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
'{} VMPricing exists already.'.format(
|
||||||
|
self.DEFAULT_VMPRICING_NAME
|
||||||
|
)
|
||||||
|
)
|
45
datacenterlight/migrations/0019_auto_20180415_2236.py
Normal file
45
datacenterlight/migrations/0019_auto_20180415_2236.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2018-04-15 22:36
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('cms', '0014_auto_20160404_1908'),
|
||||||
|
('datacenterlight', '0018_auto_20180403_1930'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='DCLCustomPricingModel',
|
||||||
|
fields=[
|
||||||
|
('cmsplugin_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='cms.CMSPlugin')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
bases=('cms.cmsplugin',),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='VMPricing',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=255, unique=True)),
|
||||||
|
('vat_inclusive', models.BooleanField(default=True)),
|
||||||
|
('vat_percentage', models.DecimalField(blank=True, decimal_places=5, default=0, max_digits=7)),
|
||||||
|
('cores_unit_price', models.DecimalField(decimal_places=5, default=0, max_digits=7)),
|
||||||
|
('ram_unit_price', models.DecimalField(decimal_places=5, default=0, max_digits=7)),
|
||||||
|
('ssd_unit_price', models.DecimalField(decimal_places=5, default=0, max_digits=7)),
|
||||||
|
('hdd_unit_price', models.DecimalField(decimal_places=6, default=0, max_digits=7)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='dclcustompricingmodel',
|
||||||
|
name='pricing',
|
||||||
|
field=models.ForeignKey(help_text='Choose a pricing that will be associated with this Calculator', on_delete=django.db.models.deletion.CASCADE, related_name='dcl_custom_pricing_vm_pricing', to='datacenterlight.VMPricing'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,5 +1,9 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class VMTemplate(models.Model):
|
class VMTemplate(models.Model):
|
||||||
name = models.CharField(max_length=50)
|
name = models.CharField(max_length=50)
|
||||||
|
@ -12,6 +16,59 @@ class VMTemplate(models.Model):
|
||||||
return vm_template
|
return vm_template
|
||||||
|
|
||||||
|
|
||||||
|
class VMPricing(models.Model):
|
||||||
|
name = models.CharField(max_length=255, unique=True)
|
||||||
|
vat_inclusive = models.BooleanField(default=True)
|
||||||
|
vat_percentage = models.DecimalField(
|
||||||
|
max_digits=7, decimal_places=5, blank=True, default=0
|
||||||
|
)
|
||||||
|
cores_unit_price = models.DecimalField(
|
||||||
|
max_digits=7, decimal_places=5, default=0
|
||||||
|
)
|
||||||
|
ram_unit_price = models.DecimalField(
|
||||||
|
max_digits=7, decimal_places=5, default=0
|
||||||
|
)
|
||||||
|
ssd_unit_price = models.DecimalField(
|
||||||
|
max_digits=7, decimal_places=5, default=0
|
||||||
|
)
|
||||||
|
hdd_unit_price = models.DecimalField(
|
||||||
|
max_digits=7, decimal_places=6, default=0
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name + ' => ' + ' - '.join([
|
||||||
|
'{}/Core'.format(self.cores_unit_price.normalize()),
|
||||||
|
'{}/GB RAM'.format(self.ram_unit_price.normalize()),
|
||||||
|
'{}/GB SSD'.format(self.ssd_unit_price.normalize()),
|
||||||
|
'{}/GB HDD'.format(self.hdd_unit_price.normalize()),
|
||||||
|
'{}% VAT'.format(self.vat_percentage.normalize())
|
||||||
|
if not self.vat_inclusive else 'VAT-Incl', ]
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_vm_pricing_by_name(cls, name):
|
||||||
|
try:
|
||||||
|
pricing = VMPricing.objects.get(name=name)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(
|
||||||
|
"Error getting VMPricing with name {name}. "
|
||||||
|
"Details: {details}. Attempting to return default"
|
||||||
|
"pricing.".format(name=name, details=str(e))
|
||||||
|
)
|
||||||
|
pricing = VMPricing.get_default_pricing()
|
||||||
|
return pricing
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_default_pricing(cls):
|
||||||
|
""" Returns the default pricing or None """
|
||||||
|
try:
|
||||||
|
default_pricing = VMPricing.objects.get(name='default')
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(str(e))
|
||||||
|
default_pricing = None
|
||||||
|
return default_pricing
|
||||||
|
|
||||||
|
|
||||||
class StripePlan(models.Model):
|
class StripePlan(models.Model):
|
||||||
"""
|
"""
|
||||||
A model to store Data Center Light's created Stripe plans
|
A model to store Data Center Light's created Stripe plans
|
||||||
|
|
|
@ -120,6 +120,11 @@
|
||||||
.header_slider .intro-cap {
|
.header_slider .intro-cap {
|
||||||
font-size: 3.25em;
|
font-size: 3.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header_slider > .carousel .item .container {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.header_slider .intro_lead {
|
.header_slider .intro_lead {
|
||||||
|
|
|
@ -1231,6 +1231,15 @@ footer {
|
||||||
background-position: center;
|
background-position: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.promo-section.promo-with-bg a {
|
||||||
|
color: #87B6EA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.promo-section.promo-with-bg a:hover,
|
||||||
|
.promo-section.promo-with-bg a:focus {
|
||||||
|
color: #77a6da;
|
||||||
|
}
|
||||||
|
|
||||||
.promo-section h3 {
|
.promo-section h3 {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 36px;
|
font-size: 36px;
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 5 KiB After Width: | Height: | Size: 5.8 KiB |
|
@ -171,7 +171,18 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function _calcPricing() {
|
function _calcPricing() {
|
||||||
var total = (cardPricing['cpu'].value * 5) + (2 * cardPricing['ram'].value) + (0.6 * cardPricing['storage'].value);
|
if(typeof window.coresUnitPrice === 'undefined'){
|
||||||
|
window.coresUnitPrice = 5;
|
||||||
|
}
|
||||||
|
if(typeof window.ramUnitPrice === 'undefined'){
|
||||||
|
window.coresUnitPrice = 2;
|
||||||
|
}
|
||||||
|
if(typeof window.ssdUnitPrice === 'undefined'){
|
||||||
|
window.ssdUnitPrice = 0.6;
|
||||||
|
}
|
||||||
|
var total = (cardPricing['cpu'].value * window.coresUnitPrice) +
|
||||||
|
(cardPricing['ram'].value * window.ramUnitPrice) +
|
||||||
|
(cardPricing['storage'].value * window.ssdUnitPrice);
|
||||||
total = parseFloat(total.toFixed(2));
|
total = parseFloat(total.toFixed(2));
|
||||||
$("#total").text(total);
|
$("#total").text(total);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ from utils.forms import UserBillingAddressForm
|
||||||
from utils.mailer import BaseEmail
|
from utils.mailer import BaseEmail
|
||||||
from utils.models import BillingAddress
|
from utils.models import BillingAddress
|
||||||
|
|
||||||
|
from .models import VMPricing
|
||||||
|
|
||||||
logger = get_task_logger(__name__)
|
logger = get_task_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,7 +58,8 @@ def create_vm_task(self, vm_template_id, user, specs, template,
|
||||||
"Running create_vm_task on {}".format(current_task.request.hostname))
|
"Running create_vm_task on {}".format(current_task.request.hostname))
|
||||||
vm_id = None
|
vm_id = None
|
||||||
try:
|
try:
|
||||||
final_price = specs.get('price')
|
final_price = (specs.get('total_price') if 'total_price' in specs
|
||||||
|
else specs.get('price'))
|
||||||
billing_address = BillingAddress(
|
billing_address = BillingAddress(
|
||||||
cardholder_name=billing_address_data['cardholder_name'],
|
cardholder_name=billing_address_data['cardholder_name'],
|
||||||
street_address=billing_address_data['street_address'],
|
street_address=billing_address_data['street_address'],
|
||||||
|
@ -94,17 +97,22 @@ def create_vm_task(self, vm_template_id, user, specs, template,
|
||||||
if vm_id is None:
|
if vm_id is None:
|
||||||
raise Exception("Could not create VM")
|
raise Exception("Could not create VM")
|
||||||
|
|
||||||
|
vm_pricing = VMPricing.get_vm_pricing_by_name(
|
||||||
|
name=specs['pricing_name']
|
||||||
|
) if 'pricing_name' in specs else VMPricing.get_default_pricing()
|
||||||
# Create a Hosting Order
|
# Create a Hosting Order
|
||||||
order = HostingOrder.create(
|
order = HostingOrder.create(
|
||||||
price=final_price,
|
price=final_price,
|
||||||
vm_id=vm_id,
|
vm_id=vm_id,
|
||||||
customer=customer,
|
customer=customer,
|
||||||
billing_address=billing_address
|
billing_address=billing_address,
|
||||||
|
vm_pricing=vm_pricing
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create a Hosting Bill
|
# Create a Hosting Bill
|
||||||
HostingBill.create(
|
HostingBill.create(
|
||||||
customer=customer, billing_address=billing_address)
|
customer=customer, billing_address=billing_address
|
||||||
|
)
|
||||||
|
|
||||||
# Create Billing Address for User if he does not have one
|
# Create Billing Address for User if he does not have one
|
||||||
if not customer.user.billing_addresses.count():
|
if not customer.user.billing_addresses.count():
|
||||||
|
@ -130,12 +138,16 @@ def create_vm_task(self, vm_template_id, user, specs, template,
|
||||||
'cores': specs.get('cpu'),
|
'cores': specs.get('cpu'),
|
||||||
'memory': specs.get('memory'),
|
'memory': specs.get('memory'),
|
||||||
'storage': specs.get('disk_size'),
|
'storage': specs.get('disk_size'),
|
||||||
'price': specs.get('price'),
|
'price': final_price,
|
||||||
'template': template.get('name'),
|
'template': template.get('name'),
|
||||||
'vm_name': vm.get('name'),
|
'vm_name': vm.get('name'),
|
||||||
'vm_id': vm['vm_id'],
|
'vm_id': vm['vm_id'],
|
||||||
'order_id': order.id
|
'order_id': order.id
|
||||||
}
|
}
|
||||||
|
if 'pricing_name' in specs:
|
||||||
|
context['pricing'] = str(VMPricing.get_vm_pricing_by_name(
|
||||||
|
name=specs['pricing_name']
|
||||||
|
))
|
||||||
email_data = {
|
email_data = {
|
||||||
'subject': settings.DCL_TEXT + " Order from %s" % context['email'],
|
'subject': settings.DCL_TEXT + " Order from %s" % context['email'],
|
||||||
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
|
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<table style="width: 100%; border-spacing: 0; border-collapse: collapse; max-width: 560px;">
|
<table style="width: 100%; border-spacing: 0; border-collapse: collapse; max-width: 560px;">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<img src="{{ base_url }}{% static 'datacenterlight/img/logo_black.png' %}" style="width: 200px; height: 50px;">
|
<img src="{{ base_url }}{% static 'datacenterlight/img/datacenterlight.png' %}" style="max-width: 200px;">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<table style="width: 100%; border-spacing: 0; border-collapse: collapse; max-width: 560px;">
|
<table style="width: 100%; border-spacing: 0; border-collapse: collapse; max-width: 560px;">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<img src="{{ base_url }}{% static 'datacenterlight/img/logo_black.png' %}" style="width: 200px; height: 50px;">
|
<img src="{{ base_url }}{% static 'datacenterlight/img/datacenterlight.png' %}" style="max-width: 200px;">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -1,4 +1,16 @@
|
||||||
{% load staticfiles i18n%}
|
{% load staticfiles i18n%}
|
||||||
|
|
||||||
|
{% if vm_pricing %}
|
||||||
|
<script type="application/javascript">
|
||||||
|
window.vat_inclusive = {% if vm_pricing.vat_inclusive %}true{% else %}false{% endif%};
|
||||||
|
window.vat_percentage = {{vm_pricing.vat_percentage|default:0}};
|
||||||
|
window.coresUnitPrice = {{vm_pricing.cores_unit_price|default:0}};
|
||||||
|
window.ramUnitPrice = {{vm_pricing.ram_unit_price|default:0}};
|
||||||
|
window.ssdUnitPrice = {{vm_pricing.ssd_unit_price|default:0}};
|
||||||
|
window.hddUnitPrice = {{vm_pricing.hdd_unit_price|default:0}};
|
||||||
|
</script>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<form id="order_form" method="POST" action="{% url 'datacenterlight:index' %}" data-toggle="validator" role="form">
|
<form id="order_form" method="POST" action="{% url 'datacenterlight:index' %}" data-toggle="validator" role="form">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="title">
|
<div class="title">
|
||||||
|
@ -7,9 +19,11 @@
|
||||||
<div class="price">
|
<div class="price">
|
||||||
<span id="total">15</span>
|
<span id="total">15</span>
|
||||||
<span>CHF/{% trans "month" %}</span>
|
<span>CHF/{% trans "month" %}</span>
|
||||||
|
{% if vm_pricing.vat_inclusive %}
|
||||||
<div class="price-text">
|
<div class="price-text">
|
||||||
<p>{% trans "VAT included" %}</p>
|
<p>{% trans "VAT included" %}</p>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="descriptions">
|
<div class="descriptions">
|
||||||
<div class="description form-group">
|
<div class="description form-group">
|
||||||
|
@ -78,5 +92,6 @@
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<input type="hidden" name="pricing_name" value="{% if vm_pricing.name %}{{vm_pricing.name}}{% else %}unknown{% endif%}"></input>
|
||||||
<input type="submit" class="btn btn-primary disabled" value="{% trans 'Continue' %}"></input>
|
<input type="submit" class="btn btn-primary disabled" value="{% trans 'Continue' %}"></input>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -78,7 +78,7 @@
|
||||||
<hr>
|
<hr>
|
||||||
<p>{% trans "Configuration"%} <strong class="pull-right">{{request.session.template.name}}</strong></p>
|
<p>{% trans "Configuration"%} <strong class="pull-right">{{request.session.template.name}}</strong></p>
|
||||||
<hr>
|
<hr>
|
||||||
<p class="last-p"><strong>{%trans "Total" %}</strong> <small>({%trans "including VAT" %})</small> <strong class="pull-right">{{request.session.specs.price|intcomma}} CHF/{% trans "Month" %}</strong></p>
|
<p class="last-p"><strong>{%trans "Total" %}</strong> <small>({% if vm_pricing.vat_inclusive %}{%trans "including VAT" %}{% else %}{%trans "excluding VAT" %}{% endif %})</small> <strong class="pull-right">{{request.session.specs.price|intcomma}} CHF/{% trans "Month" %}</strong></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -65,9 +65,19 @@
|
||||||
<span>{% trans "Disk space" %}: </span>
|
<span>{% trans "Disk space" %}: </span>
|
||||||
<span class="pull-right">{{vm.disk_size|intcomma}} GB</span>
|
<span class="pull-right">{{vm.disk_size|intcomma}} GB</span>
|
||||||
</p>
|
</p>
|
||||||
|
{% if vm.vat > 0 %}
|
||||||
<p>
|
<p>
|
||||||
<span>{% trans "Total" %}</span>
|
<strong>{% trans "Subtotal" %}: </strong>
|
||||||
<span class="pull-right">{{vm.price|intcomma}} CHF</span>
|
<span class="pull-right">{{vm.price|floatformat:2|intcomma}} CHF</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span>{% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%): </span>
|
||||||
|
<span class="pull-right">{{vm.vat|floatformat:2|intcomma}} CHF</span>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
<p>
|
||||||
|
<strong>{% trans "Total" %}</strong>
|
||||||
|
<span class="pull-right">{{vm.total_price|floatformat:2|intcomma}} CHF</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -78,7 +88,7 @@
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-8">
|
||||||
<div class="dcl-place-order-text">{% blocktrans with vm_price=request.session.specs.price %}By clicking "Place order" this plan will charge your credit card account with the fee of {{ vm_price }}CHF/month{% endblocktrans %}.</div>
|
<div class="dcl-place-order-text">{% blocktrans with vm_total_price=vm.total_price|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with the fee of {{vm_total_price}} CHF/month{% endblocktrans %}.</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-4 order-confirm-btn text-right">
|
<div class="col-sm-4 order-confirm-btn text-right">
|
||||||
<button class="btn choice-btn" id="btn-create-vm" data-toggle="modal" data-target="#createvm-modal">
|
<button class="btn choice-btn" id="btn-create-vm" data-toggle="modal" data-target="#createvm-modal">
|
||||||
|
|
|
@ -19,11 +19,11 @@ from hosting.models import HostingOrder
|
||||||
from membership.models import CustomUser, StripeCustomer
|
from membership.models import CustomUser, StripeCustomer
|
||||||
from opennebula_api.serializers import VMTemplateSerializer
|
from opennebula_api.serializers import VMTemplateSerializer
|
||||||
from utils.forms import BillingAddressForm, BillingAddressFormSignup
|
from utils.forms import BillingAddressForm, BillingAddressFormSignup
|
||||||
from utils.hosting_utils import get_vm_price
|
from utils.hosting_utils import get_vm_price_with_vat
|
||||||
from utils.stripe_utils import StripeUtils
|
from utils.stripe_utils import StripeUtils
|
||||||
from utils.tasks import send_plain_email_task
|
from utils.tasks import send_plain_email_task
|
||||||
from .forms import ContactForm
|
from .forms import ContactForm
|
||||||
from .models import VMTemplate
|
from .models import VMTemplate, VMPricing
|
||||||
from .utils import get_cms_integration
|
from .utils import get_cms_integration
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -93,7 +93,8 @@ class IndexView(CreateView):
|
||||||
|
|
||||||
@cache_control(no_cache=True, must_revalidate=True, no_store=True)
|
@cache_control(no_cache=True, must_revalidate=True, no_store=True)
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
for session_var in ['specs', 'user', 'billing_address_data']:
|
for session_var in ['specs', 'user', 'billing_address_data',
|
||||||
|
'pricing_name']:
|
||||||
if session_var in request.session:
|
if session_var in request.session:
|
||||||
del request.session[session_var]
|
del request.session[session_var]
|
||||||
return HttpResponseRedirect(reverse('datacenterlight:cms_index'))
|
return HttpResponseRedirect(reverse('datacenterlight:cms_index'))
|
||||||
|
@ -106,12 +107,30 @@ class IndexView(CreateView):
|
||||||
storage = request.POST.get('storage')
|
storage = request.POST.get('storage')
|
||||||
storage_field = forms.IntegerField(validators=[self.validate_storage])
|
storage_field = forms.IntegerField(validators=[self.validate_storage])
|
||||||
template_id = int(request.POST.get('config'))
|
template_id = int(request.POST.get('config'))
|
||||||
|
pricing_name = request.POST.get('pricing_name')
|
||||||
|
vm_pricing = VMPricing.get_vm_pricing_by_name(pricing_name)
|
||||||
|
|
||||||
template = VMTemplate.objects.filter(
|
template = VMTemplate.objects.filter(
|
||||||
opennebula_vm_template_id=template_id
|
opennebula_vm_template_id=template_id
|
||||||
).first()
|
).first()
|
||||||
template_data = VMTemplateSerializer(template).data
|
template_data = VMTemplateSerializer(template).data
|
||||||
referer_url = request.META['HTTP_REFERER']
|
referer_url = request.META['HTTP_REFERER']
|
||||||
|
|
||||||
|
if vm_pricing is None:
|
||||||
|
vm_pricing_name_msg = _(
|
||||||
|
"Incorrect pricing name. Please contact support"
|
||||||
|
"{support_email}".format(
|
||||||
|
support_email=settings.DCL_SUPPORT_FROM_ADDRESS
|
||||||
|
)
|
||||||
|
)
|
||||||
|
messages.add_message(
|
||||||
|
self.request, messages.ERROR, vm_pricing_name_msg,
|
||||||
|
extra_tags='pricing'
|
||||||
|
)
|
||||||
|
return HttpResponseRedirect(referer_url + "#order_form")
|
||||||
|
else:
|
||||||
|
vm_pricing_name = vm_pricing.name
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cores = cores_field.clean(cores)
|
cores = cores_field.clean(cores)
|
||||||
except ValidationError as err:
|
except ValidationError as err:
|
||||||
|
@ -139,14 +158,21 @@ class IndexView(CreateView):
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(referer_url + "#order_form")
|
return HttpResponseRedirect(referer_url + "#order_form")
|
||||||
|
|
||||||
amount_to_be_charged = get_vm_price(
|
price, vat, vat_percent = get_vm_price_with_vat(
|
||||||
cpu=cores, memory=memory, disk_size=storage
|
cpu=cores,
|
||||||
|
memory=memory,
|
||||||
|
ssd_size=storage,
|
||||||
|
pricing_name=vm_pricing_name
|
||||||
)
|
)
|
||||||
specs = {
|
specs = {
|
||||||
'cpu': cores,
|
'cpu': cores,
|
||||||
'memory': memory,
|
'memory': memory,
|
||||||
'disk_size': storage,
|
'disk_size': storage,
|
||||||
'price': amount_to_be_charged
|
'price': price,
|
||||||
|
'vat': vat,
|
||||||
|
'vat_percent': vat_percent,
|
||||||
|
'total_price': price + vat,
|
||||||
|
'pricing_name': vm_pricing_name
|
||||||
}
|
}
|
||||||
request.session['specs'] = specs
|
request.session['specs'] = specs
|
||||||
request.session['template'] = template_data
|
request.session['template'] = template_data
|
||||||
|
@ -220,7 +246,10 @@ class PaymentOrderView(FormView):
|
||||||
'site_url': reverse('datacenterlight:index'),
|
'site_url': reverse('datacenterlight:index'),
|
||||||
'login_form': HostingUserLoginForm(prefix='login_form'),
|
'login_form': HostingUserLoginForm(prefix='login_form'),
|
||||||
'billing_address_form': billing_address_form,
|
'billing_address_form': billing_address_form,
|
||||||
'cms_integration': get_cms_integration('default')
|
'cms_integration': get_cms_integration('default'),
|
||||||
|
'vm_pricing': VMPricing.get_vm_pricing_by_name(
|
||||||
|
self.request.session['specs']['pricing_name']
|
||||||
|
)
|
||||||
})
|
})
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
@ -393,7 +422,7 @@ class OrderConfirmationView(DetailView):
|
||||||
cpu = specs.get('cpu')
|
cpu = specs.get('cpu')
|
||||||
memory = specs.get('memory')
|
memory = specs.get('memory')
|
||||||
disk_size = specs.get('disk_size')
|
disk_size = specs.get('disk_size')
|
||||||
amount_to_be_charged = specs.get('price')
|
amount_to_be_charged = specs.get('total_price')
|
||||||
plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu,
|
plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu,
|
||||||
memory=memory,
|
memory=memory,
|
||||||
disk_size=disk_size)
|
disk_size=disk_size)
|
||||||
|
@ -489,7 +518,7 @@ class OrderConfirmationView(DetailView):
|
||||||
stripe_subscription_obj.id, card_details_dict)
|
stripe_subscription_obj.id, card_details_dict)
|
||||||
for session_var in ['specs', 'template', 'billing_address',
|
for session_var in ['specs', 'template', 'billing_address',
|
||||||
'billing_address_data',
|
'billing_address_data',
|
||||||
'token', 'customer']:
|
'token', 'customer', 'pricing_name']:
|
||||||
if session_var in request.session:
|
if session_var in request.session:
|
||||||
del request.session[session_var]
|
del request.session[session_var]
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,7 @@ urlpatterns += i18n_patterns(
|
||||||
include('ungleich_page.urls',
|
include('ungleich_page.urls',
|
||||||
namespace='ungleich_page'),
|
namespace='ungleich_page'),
|
||||||
name='ungleich_page'),
|
name='ungleich_page'),
|
||||||
|
url(r'^blog/', include('djangocms_blog.urls', namespace='djangocms_blog')),
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns += [
|
urlpatterns += [
|
||||||
|
|
23
hosting/migrations/0044_hostingorder_vm_pricing.py
Normal file
23
hosting/migrations/0044_hostingorder_vm_pricing.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2018-04-16 00:22
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('datacenterlight', '0019_auto_20180415_2236'),
|
||||||
|
('hosting', '0043_vmdetail'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='hostingorder',
|
||||||
|
name='vm_pricing',
|
||||||
|
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='datacenterlight.VMPricing'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
]
|
|
@ -6,6 +6,8 @@ from django.db import models
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from Crypto.PublicKey import RSA
|
from Crypto.PublicKey import RSA
|
||||||
|
|
||||||
|
from datacenterlight.models import VMPricing
|
||||||
from membership.models import StripeCustomer, CustomUser
|
from membership.models import StripeCustomer, CustomUser
|
||||||
from utils.models import BillingAddress
|
from utils.models import BillingAddress
|
||||||
from utils.mixins import AssignPermissionsMixin
|
from utils.mixins import AssignPermissionsMixin
|
||||||
|
@ -53,6 +55,7 @@ class HostingOrder(AssignPermissionsMixin, models.Model):
|
||||||
stripe_charge_id = models.CharField(max_length=100, null=True)
|
stripe_charge_id = models.CharField(max_length=100, null=True)
|
||||||
price = models.FloatField()
|
price = models.FloatField()
|
||||||
subscription_id = models.CharField(max_length=100, null=True)
|
subscription_id = models.CharField(max_length=100, null=True)
|
||||||
|
vm_pricing = models.ForeignKey(VMPricing)
|
||||||
|
|
||||||
permissions = ('view_hostingorder',)
|
permissions = ('view_hostingorder',)
|
||||||
|
|
||||||
|
@ -70,12 +73,13 @@ class HostingOrder(AssignPermissionsMixin, models.Model):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, price=None, vm_id=None, customer=None,
|
def create(cls, price=None, vm_id=None, customer=None,
|
||||||
billing_address=None):
|
billing_address=None, vm_pricing=None):
|
||||||
instance = cls.objects.create(
|
instance = cls.objects.create(
|
||||||
price=price,
|
price=price,
|
||||||
vm_id=vm_id,
|
vm_id=vm_id,
|
||||||
customer=customer,
|
customer=customer,
|
||||||
billing_address=billing_address
|
billing_address=billing_address,
|
||||||
|
vm_pricing=vm_pricing
|
||||||
)
|
)
|
||||||
instance.assign_permissions(customer.user)
|
instance.assign_permissions(customer.user)
|
||||||
return instance
|
return instance
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<table style="width: 100%; border-spacing: 0; border-collapse: collapse; max-width: 560px;">
|
<table style="width: 100%; border-spacing: 0; border-collapse: collapse; max-width: 560px;">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<img src="{{ base_url }}{% static 'datacenterlight/img/logo_black.png' %}" style="width: 200px; height: 50px;">
|
<img src="{{ base_url }}{% static 'datacenterlight/img/datacenterlight.png' %}" style="max-width: 200px;">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<table style="width: 100%; border-spacing: 0; border-collapse: collapse; max-width: 560px;">
|
<table style="width: 100%; border-spacing: 0; border-collapse: collapse; max-width: 560px;">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<img src="{{base_url}}{% static 'datacenterlight/img/logo_black.png' %}" style="width: 200px; height: 50px;">
|
<img src="{{ base_url }}{% static 'datacenterlight/img/datacenterlight.png' %}" style="max-width: 200px;">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<table style="width: 100%; border-spacing: 0; border-collapse: collapse; max-width: 560px;">
|
<table style="width: 100%; border-spacing: 0; border-collapse: collapse; max-width: 560px;">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<img src="{{ base_url }}{% static 'datacenterlight/img/logo_black.png' %}" style="width: 200px; height: 50px;">
|
<img src="{{ base_url }}{% static 'datacenterlight/img/datacenterlight.png' %}" style="max-width: 200px;">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -74,7 +74,7 @@
|
||||||
<center style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
|
<center style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
|
||||||
<table cellpadding="0" cellspacing="0" width="600" class="w320" style="border-collapse: collapse !important; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;"><tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
|
<table cellpadding="0" cellspacing="0" width="600" class="w320" style="border-collapse: collapse !important; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;"><tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
|
||||||
<td class="pull-left mobile-header-padding-left" style="vertical-align: middle; border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; text-align: left; line-height: 21px; width: 290px; padding-left: 10px;" align="left" valign="middle">
|
<td class="pull-left mobile-header-padding-left" style="vertical-align: middle; border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; text-align: left; line-height: 21px; width: 290px; padding-left: 10px;" align="left" valign="middle">
|
||||||
<a href="{{base_url}}" style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; color: #676767; text-decoration: none !important;"><img width="137" src="{{base_url}}{% static "hosting/img/logo_black.png" %}" alt="logo" style="max-width: 600px; outline: none; text-decoration: none; -ms-interpolation-mode: bicubic; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; border: none;"></a>
|
<a href="{{base_url}}" style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; color: #676767; text-decoration: none !important;"><img width="137" src="{{base_url}}{% static 'hosting/img/datacenterlight.png' %}" alt="logo" style="max-width: 600px; outline: none; text-decoration: none; -ms-interpolation-mode: bicubic; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; border: none;"></a>
|
||||||
</td>
|
</td>
|
||||||
<td class="pull-right mobile-header-padding-right" style="color: #4d4d4d; border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; text-align: right; line-height: 21px; width: 290px; padding-left: 10px;" align="right">
|
<td class="pull-right mobile-header-padding-right" style="color: #4d4d4d; border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; text-align: right; line-height: 21px; width: 290px; padding-left: 10px;" align="right">
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -127,9 +127,19 @@
|
||||||
<span>{% trans "Disk space" %}: </span>
|
<span>{% trans "Disk space" %}: </span>
|
||||||
<span class="pull-right">{{vm.disk_size}} GB</span>
|
<span class="pull-right">{{vm.disk_size}} GB</span>
|
||||||
</p>
|
</p>
|
||||||
|
{% if vm.vat > 0 %}
|
||||||
<p>
|
<p>
|
||||||
<span>{% trans "Total" %}</span>
|
<strong>{% trans "Subtotal" %}: </strong>
|
||||||
<span class="pull-right">{{vm.price|intcomma}} CHF</span>
|
<span class="pull-right">{{vm.price|floatformat:2|intcomma}} CHF</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span>{% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%): </span>
|
||||||
|
<span class="pull-right">{{vm.vat|floatformat:2|intcomma}} CHF</span>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
<p>
|
||||||
|
<strong>{% trans "Total" %}</strong>
|
||||||
|
<span class="pull-right">{% if vm.total_price %}{{vm.total_price|floatformat:2|intcomma}}{% else %}{{vm.price|floatformat:2|intcomma}}{% endif %} CHF</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td class="xs-td-inline" data-header="{% trans 'Order Nr.' %}">{{ order.id }}</td>
|
<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" data-header="{% trans 'Date' %}">{{ order.created_at | date:"M d, Y H:i" }}</td>
|
||||||
<td class="xs-td-smallhalf" data-header="{% trans 'Amount' %}">{{ order.price|intcomma }}</td>
|
<td class="xs-td-smallhalf" data-header="{% trans 'Amount' %}">{{ order.price|floatformat:2|intcomma }}</td>
|
||||||
<td class="text-right last-td">
|
<td class="text-right last-td">
|
||||||
<a class="btn btn-order-detail" href="{% url 'hosting:orders' order.pk %}">{% trans 'See Invoice' %}</a>
|
<a class="btn btn-order-detail" href="{% url 'hosting:orders' order.pk %}">{% trans 'See Invoice' %}</a>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
<h2 class="vm-detail-title">{% trans "Billing" %} <img src="{% static 'hosting/img/billing.svg' %}" class="un-icon"></h2>
|
<h2 class="vm-detail-title">{% trans "Billing" %} <img src="{% static 'hosting/img/billing.svg' %}" class="un-icon"></h2>
|
||||||
<div class="vm-vmid">
|
<div class="vm-vmid">
|
||||||
<div class="vm-item-subtitle">{% trans "Current Pricing" %}</div>
|
<div class="vm-item-subtitle">{% trans "Current Pricing" %}</div>
|
||||||
<div class="vm-item-lg">{{virtual_machine.price|floatformat|intcomma}} CHF/{% trans "Month" %}</div>
|
<div class="vm-item-lg">{{order.price|floatformat:2|intcomma}} CHF/{% trans "Month" %}</div>
|
||||||
<a class="btn btn-vm-invoice" href="{% url 'hosting:orders' order.pk %}">{% trans "See Invoice" %}</a>
|
<a class="btn btn-vm-invoice" href="{% url 'hosting:orders' order.pk %}">{% trans "See Invoice" %}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -42,7 +42,7 @@ from utils.forms import (
|
||||||
BillingAddressForm, PasswordResetRequestForm, UserBillingAddressForm,
|
BillingAddressForm, PasswordResetRequestForm, UserBillingAddressForm,
|
||||||
ResendActivationEmailForm
|
ResendActivationEmailForm
|
||||||
)
|
)
|
||||||
from utils.hosting_utils import get_vm_price
|
from utils.hosting_utils import get_vm_price, get_vm_price_with_vat
|
||||||
from utils.mailer import BaseEmail
|
from utils.mailer import BaseEmail
|
||||||
from utils.stripe_utils import StripeUtils
|
from utils.stripe_utils import StripeUtils
|
||||||
from utils.tasks import send_plain_email_task
|
from utils.tasks import send_plain_email_task
|
||||||
|
@ -749,11 +749,17 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView):
|
||||||
context['vm'] = vm_detail.__dict__
|
context['vm'] = vm_detail.__dict__
|
||||||
context['vm']['name'] = '{}-{}'.format(
|
context['vm']['name'] = '{}-{}'.format(
|
||||||
context['vm']['configuration'], context['vm']['vm_id'])
|
context['vm']['configuration'], context['vm']['vm_id'])
|
||||||
context['vm']['price'] = get_vm_price(
|
price, vat, vat_percent = get_vm_price_with_vat(
|
||||||
cpu=context['vm']['cores'],
|
cpu=context['vm']['cores'],
|
||||||
disk_size=context['vm']['disk_size'],
|
ssd_size=context['vm']['disk_size'],
|
||||||
memory=context['vm']['memory']
|
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']['vat_percent'] = vat_percent
|
||||||
|
context['vm']['total_price'] = price + vat
|
||||||
context['subscription_end_date'] = vm_detail.end_date()
|
context['subscription_end_date'] = vm_detail.end_date()
|
||||||
except VMDetail.DoesNotExist:
|
except VMDetail.DoesNotExist:
|
||||||
try:
|
try:
|
||||||
|
@ -762,6 +768,17 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView):
|
||||||
)
|
)
|
||||||
vm = manager.get_vm(obj.vm_id)
|
vm = manager.get_vm(obj.vm_id)
|
||||||
context['vm'] = VirtualMachineSerializer(vm).data
|
context['vm'] = VirtualMachineSerializer(vm).data
|
||||||
|
price, vat, vat_percent = 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']['vat_percent'] = vat_percent
|
||||||
|
context['vm']['total_price'] = price + vat
|
||||||
except WrongIdError:
|
except WrongIdError:
|
||||||
messages.error(
|
messages.error(
|
||||||
self.request,
|
self.request,
|
||||||
|
@ -1100,7 +1117,8 @@ class VirtualMachineView(LoginRequiredMixin, View):
|
||||||
context = {
|
context = {
|
||||||
'virtual_machine': serializer.data,
|
'virtual_machine': serializer.data,
|
||||||
'order': HostingOrder.objects.get(
|
'order': HostingOrder.objects.get(
|
||||||
vm_id=serializer.data['vm_id'])
|
vm_id=serializer.data['vm_id']
|
||||||
|
)
|
||||||
}
|
}
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.debug("Exception generated {}".format(str(ex)))
|
logger.debug("Exception generated {}".format(str(ex)))
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import decimal
|
||||||
import logging
|
import logging
|
||||||
from oca.pool import WrongIdError
|
from oca.pool import WrongIdError
|
||||||
|
|
||||||
|
from datacenterlight.models import VMPricing
|
||||||
from hosting.models import UserHostingKey, VMDetail
|
from hosting.models import UserHostingKey, VMDetail
|
||||||
from opennebula_api.serializers import VirtualMachineSerializer
|
from opennebula_api.serializers import VirtualMachineSerializer
|
||||||
|
|
||||||
|
@ -49,14 +51,74 @@ def get_or_create_vm_detail(user, manager, vm_id):
|
||||||
return vm_detail_obj
|
return vm_detail_obj
|
||||||
|
|
||||||
|
|
||||||
def get_vm_price(cpu, memory, disk_size):
|
def get_vm_price(cpu, memory, disk_size, hdd_size=0, pricing_name='default'):
|
||||||
"""
|
"""
|
||||||
A helper function that computes price of a VM from given cpu, ram and
|
A helper function that computes price of a VM from given cpu, ram and
|
||||||
ssd parameters
|
ssd parameters
|
||||||
|
|
||||||
:param cpu: Number of cores of the VM
|
:param cpu: Number of cores of the VM
|
||||||
:param memory: RAM of the VM
|
:param memory: RAM of the VM
|
||||||
:param disk_size: Disk space of the VM
|
:param disk_size: Disk space of the VM (SSD)
|
||||||
|
:param hdd_size: The HDD size
|
||||||
|
:param pricing_name: The pricing name to be used
|
||||||
:return: The price of the VM
|
:return: The price of the VM
|
||||||
"""
|
"""
|
||||||
return (cpu * 5) + (memory * 2) + (disk_size * 0.6)
|
try:
|
||||||
|
pricing = VMPricing.objects.get(name=pricing_name)
|
||||||
|
except Exception as ex:
|
||||||
|
logger.error(
|
||||||
|
"Error getting VMPricing object for {pricing_name}."
|
||||||
|
"Details: {details}".format(
|
||||||
|
pricing_name=pricing_name, details=str(ex)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
price = ((decimal.Decimal(cpu) * pricing.cores_unit_price) +
|
||||||
|
(decimal.Decimal(memory) * pricing.ram_unit_price) +
|
||||||
|
(decimal.Decimal(disk_size) * pricing.ssd_unit_price) +
|
||||||
|
(decimal.Decimal(hdd_size) * pricing.hdd_unit_price))
|
||||||
|
cents = decimal.Decimal('.01')
|
||||||
|
price = price.quantize(cents, decimal.ROUND_HALF_UP)
|
||||||
|
return float(price)
|
||||||
|
|
||||||
|
|
||||||
|
def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0,
|
||||||
|
pricing_name='default'):
|
||||||
|
"""
|
||||||
|
A helper function that computes price of a VM from given cpu, ram and
|
||||||
|
ssd, hdd and the pricing parameters
|
||||||
|
|
||||||
|
:param cpu: Number of cores of the VM
|
||||||
|
:param memory: RAM of the VM
|
||||||
|
:param ssd_size: Disk space of the VM (SSD)
|
||||||
|
:param hdd_size: The HDD size
|
||||||
|
:param pricing_name: The pricing name to be used
|
||||||
|
:return: The a tuple containing the price of the VM, the VAT and the
|
||||||
|
VAT percentage
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
pricing = VMPricing.objects.get(name=pricing_name)
|
||||||
|
except Exception as ex:
|
||||||
|
logger.error(
|
||||||
|
"Error getting VMPricing object for {pricing_name}."
|
||||||
|
"Details: {details}".format(
|
||||||
|
pricing_name=pricing_name, details=str(ex)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
price = ((decimal.Decimal(cpu) * pricing.cores_unit_price) +
|
||||||
|
(decimal.Decimal(memory) * pricing.ram_unit_price) +
|
||||||
|
(decimal.Decimal(ssd_size) * pricing.ssd_unit_price) +
|
||||||
|
(decimal.Decimal(hdd_size) * pricing.hdd_unit_price))
|
||||||
|
if pricing.vat_inclusive:
|
||||||
|
vat = decimal.Decimal(0)
|
||||||
|
vat_percent = decimal.Decimal(0)
|
||||||
|
else:
|
||||||
|
vat = price * pricing.vat_percentage * decimal.Decimal(0.01)
|
||||||
|
vat_percent = pricing.vat_percentage
|
||||||
|
|
||||||
|
cents = decimal.Decimal('.01')
|
||||||
|
price = price.quantize(cents, decimal.ROUND_HALF_UP)
|
||||||
|
vat = vat.quantize(cents, decimal.ROUND_HALF_UP)
|
||||||
|
return float(price), float(vat), float(vat_percent)
|
||||||
|
|
Loading…
Reference in a new issue