merge master
This commit is contained in:
		
				commit
				
					
						275ea47e41
					
				
			
		
					 32 changed files with 530 additions and 125 deletions
				
			
		
							
								
								
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -41,4 +41,5 @@ secret-key
 | 
			
		|||
/utils/optimize/
 | 
			
		||||
 | 
			
		||||
# 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
 | 
			
		||||
    * #4396: [ungleich] add favicon to ungleich blog
 | 
			
		||||
    * #4327: [dcl] fix navbar logo repeat
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ from django.contrib import admin
 | 
			
		|||
from cms.admin.placeholderadmin import PlaceholderAdminMixin
 | 
			
		||||
from cms.extensions import PageExtensionAdmin
 | 
			
		||||
from .cms_models import CMSIntegration, CMSFaviconExtension
 | 
			
		||||
from .models import VMPricing
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CMSIntegrationAdmin(PlaceholderAdminMixin, admin.ModelAdmin):
 | 
			
		||||
| 
						 | 
				
			
			@ -14,3 +15,4 @@ class CMSFaviconExtensionAdmin(PageExtensionAdmin):
 | 
			
		|||
 | 
			
		||||
admin.site.register(CMSIntegration, CMSIntegrationAdmin)
 | 
			
		||||
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.image import FilerImageField
 | 
			
		||||
 | 
			
		||||
from datacenterlight.models import VMPricing
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CMSIntegration(models.Model):
 | 
			
		||||
    name = models.CharField(
 | 
			
		||||
| 
						 | 
				
			
			@ -284,3 +286,12 @@ class DCLSectionPromoPluginModel(CMSPlugin):
 | 
			
		|||
        if self.background_image:
 | 
			
		||||
            extra_classes += ' promo-with-bg'
 | 
			
		||||
        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,
 | 
			
		||||
    DCLSectionIconPluginModel, DCLSectionImagePluginModel,
 | 
			
		||||
    DCLSectionPluginModel, DCLNavbarPluginModel,
 | 
			
		||||
    DCLSectionPromoPluginModel
 | 
			
		||||
    DCLSectionPromoPluginModel, DCLCustomPricingModel
 | 
			
		||||
)
 | 
			
		||||
from .models import VMTemplate
 | 
			
		||||
from .models import VMTemplate, VMPricing
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@plugin_pool.register_plugin
 | 
			
		||||
| 
						 | 
				
			
			@ -75,13 +75,13 @@ class DCLSectionPromoPlugin(CMSPluginBase):
 | 
			
		|||
@plugin_pool.register_plugin
 | 
			
		||||
class DCLCalculatorPlugin(CMSPluginBase):
 | 
			
		||||
    module = "Datacenterlight"
 | 
			
		||||
    name = "DCL Calculator Plugin"
 | 
			
		||||
    name = "DCL Calculator Section Plugin"
 | 
			
		||||
    model = DCLSectionPluginModel
 | 
			
		||||
    render_template = "datacenterlight/cms/calculator.html"
 | 
			
		||||
    cache = False
 | 
			
		||||
    allow_children = True
 | 
			
		||||
    child_classes = [
 | 
			
		||||
        'DCLSectionPromoPlugin', 'UngleichHTMLPlugin'
 | 
			
		||||
        'DCLSectionPromoPlugin', 'UngleichHTMLPlugin', 'DCLCustomPricingPlugin'
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    def render(self, context, instance, placeholder):
 | 
			
		||||
| 
						 | 
				
			
			@ -89,15 +89,38 @@ class DCLCalculatorPlugin(CMSPluginBase):
 | 
			
		|||
            context, instance, placeholder
 | 
			
		||||
        )
 | 
			
		||||
        context['templates'] = VMTemplate.objects.all()
 | 
			
		||||
        context['children_to_side'] = []
 | 
			
		||||
        context['children_to_content'] = []
 | 
			
		||||
        pricing_plugin_model = None
 | 
			
		||||
        if instance.child_plugin_instances is not None:
 | 
			
		||||
            context['children_to_content'].extend(
 | 
			
		||||
                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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@plugin_pool.register_plugin
 | 
			
		||||
class DCLCustomPricingPlugin(CMSPluginBase):
 | 
			
		||||
    module = "Datacenterlight"
 | 
			
		||||
    name = "DCL Custom Pricing Plugin"
 | 
			
		||||
    model = DCLCustomPricingModel
 | 
			
		||||
    render_plugin = False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@plugin_pool.register_plugin
 | 
			
		||||
class DCLBannerListPlugin(CMSPluginBase):
 | 
			
		||||
    module = "Datacenterlight"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,7 @@ msgid ""
 | 
			
		|||
msgstr ""
 | 
			
		||||
"Project-Id-Version: PACKAGE VERSION\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"
 | 
			
		||||
"Last-Translator: b'Anonymous User <coder.purple+25@gmail.com>'\n"
 | 
			
		||||
"Language-Team: LANGUAGE <LL@li.org>\n"
 | 
			
		||||
| 
						 | 
				
			
			@ -72,9 +72,9 @@ msgstr "Data Center Light Account Aktivierung"
 | 
			
		|||
 | 
			
		||||
#, python-format
 | 
			
		||||
msgid ""
 | 
			
		||||
"You can activate your Data Center Light account by clicking <a "
 | 
			
		||||
"href=\"%(base_url)s%(activation_link)s\" style=\"text-decoration: none; "
 | 
			
		||||
"color: #4382c8; font-weight: 400;\">here</a>."
 | 
			
		||||
"You can activate your Data Center Light account by clicking <a href="
 | 
			
		||||
"\"%(base_url)s%(activation_link)s\" style=\"text-decoration: none; color: "
 | 
			
		||||
"#4382c8; font-weight: 400;\">here</a>."
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Klicke <a href=\"%(base_url)s%(activation_link)s\"style=\"text-decoration: "
 | 
			
		||||
"none; color: #4382c8; font-weight: 400;\">hier</a> um deinen Data Center "
 | 
			
		||||
| 
						 | 
				
			
			@ -97,12 +97,13 @@ msgstr "Deine E-Mail-Adresse"
 | 
			
		|||
msgid "Password"
 | 
			
		||||
msgstr "Passwort"
 | 
			
		||||
 | 
			
		||||
#, python-format
 | 
			
		||||
msgid ""
 | 
			
		||||
"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>."
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Du kannst dein Passwort <a href=\"%(base_url)s%(reset_password_url)s\" "
 | 
			
		||||
"style=\"text-decoration: none; color: #4382c8; font-weight: 400;\">hier</a> "
 | 
			
		||||
"Du kannst dein Passwort <a href=\"%(base_url)s%(reset_password_url)s\" style="
 | 
			
		||||
"\"text-decoration: none; color: #4382c8; font-weight: 400;\">hier</a> "
 | 
			
		||||
"zurücksetzen."
 | 
			
		||||
 | 
			
		||||
msgid "Your Data Center Light Team"
 | 
			
		||||
| 
						 | 
				
			
			@ -160,21 +161,6 @@ msgstr "Weiter"
 | 
			
		|||
msgid "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"
 | 
			
		||||
msgstr "Kontakt"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -184,6 +170,9 @@ msgstr "Nutzungsbedingungen"
 | 
			
		|||
msgid "Finally, an affordable VM hosting in Switzerland!"
 | 
			
		||||
msgstr "Endlich: bezahlbares VM Hosting in der Schweiz"
 | 
			
		||||
 | 
			
		||||
msgid "Highlights"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
msgid "I want it!"
 | 
			
		||||
msgstr "Das will ich haben!"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -203,8 +192,8 @@ msgid ""
 | 
			
		|||
"order to make it more sustainable and affordable at the same time."
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Ist kreativ, indem es sich ein modernes und alternatives Layout zu Nutze "
 | 
			
		||||
"macht um Nachhaltigkeit zu fördern und somit erschwingliche Preise bieten zu"
 | 
			
		||||
" können.
"
 | 
			
		||||
"macht um Nachhaltigkeit zu fördern und somit erschwingliche Preise bieten zu "
 | 
			
		||||
"können.
"
 | 
			
		||||
 | 
			
		||||
msgid ""
 | 
			
		||||
"Cuts down the costs for you by using FOSS (Free Open Source Software) "
 | 
			
		||||
| 
						 | 
				
			
			@ -214,6 +203,9 @@ msgstr ""
 | 
			
		|||
"mit FOSS (Free Open Source Software) arbeitet und wir daher auf "
 | 
			
		||||
"Lizenzgebühren verzichten können.
"
 | 
			
		||||
 | 
			
		||||
msgid "Scale out"
 | 
			
		||||
msgstr "Skalierung"
 | 
			
		||||
 | 
			
		||||
msgid ""
 | 
			
		||||
"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 "
 | 
			
		||||
| 
						 | 
				
			
			@ -223,6 +215,9 @@ msgstr ""
 | 
			
		|||
"erschwingliche Systeme. Bei grösserer Auslastung werden mehr "
 | 
			
		||||
"Standardkomponenten hinzugekauft und skalieren so das Datencenter."
 | 
			
		||||
 | 
			
		||||
msgid "Reliable and light"
 | 
			
		||||
msgstr "Zuverlässig und leicht"
 | 
			
		||||
 | 
			
		||||
msgid ""
 | 
			
		||||
"Our VMs are located in Switzerland, with reliable power supply and fast "
 | 
			
		||||
"internet connection. Our VM costs less thanks to our featherlight "
 | 
			
		||||
| 
						 | 
				
			
			@ -232,8 +227,7 @@ msgstr ""
 | 
			
		|||
"Energieversorgung, sowie schneller Internetverbindung ausgestattet. Unser "
 | 
			
		||||
"Angebot ist aufgrund unserer leichten Infrastruktur überaus kostengünstig."
 | 
			
		||||
 | 
			
		||||
msgid ""
 | 
			
		||||
"Simple and affordable: Try our virtual machine with featherlight price."
 | 
			
		||||
msgid "Simple and affordable: Try our virtual machine with featherlight price."
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Einfach und bezahlbar: Teste nun unsere virtuellen Maschinen mit "
 | 
			
		||||
"federleichten Preisen."
 | 
			
		||||
| 
						 | 
				
			
			@ -314,6 +308,9 @@ msgstr "Gesamt"
 | 
			
		|||
msgid "including VAT"
 | 
			
		||||
msgstr "inkl. Mehrwertsteuer"
 | 
			
		||||
 | 
			
		||||
msgid "excluding VAT"
 | 
			
		||||
msgstr "exkl. Mehrwertsteuer"
 | 
			
		||||
 | 
			
		||||
msgid "Month"
 | 
			
		||||
msgstr "Monat"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -321,20 +318,20 @@ msgid "Credit Card"
 | 
			
		|||
msgstr "Kreditkarte"
 | 
			
		||||
 | 
			
		||||
msgid ""
 | 
			
		||||
"Please fill in your credit card information below. We are using <a "
 | 
			
		||||
"href=\"https://stripe.com\" target=\"_blank\">Stripe</a> for payment and do "
 | 
			
		||||
"not store your information in our database."
 | 
			
		||||
"Please fill in your credit card information below. We are using <a href="
 | 
			
		||||
"\"https://stripe.com\" target=\"_blank\">Stripe</a> for payment and do not "
 | 
			
		||||
"store your information in our database."
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Bitte fülle Deine Kreditkarteninformationen unten aus. Wir nutzen <a "
 | 
			
		||||
"href=\"https://stripe.com\" target=\"_blank\">Stripe</a> für die Bezahlung "
 | 
			
		||||
"und speichern keine Informationen in unserer Datenbank."
 | 
			
		||||
"Bitte fülle Deine Kreditkarteninformationen unten aus. Wir nutzen <a href="
 | 
			
		||||
"\"https://stripe.com\" target=\"_blank\">Stripe</a> für die Bezahlung und "
 | 
			
		||||
"speichern keine Informationen in unserer Datenbank."
 | 
			
		||||
 | 
			
		||||
msgid ""
 | 
			
		||||
"You are not making any payment yet. After submitting your card information, "
 | 
			
		||||
"you will be taken to the Confirm Order 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 "Card Number"
 | 
			
		||||
msgstr "Kreditkartennummer"
 | 
			
		||||
| 
						 | 
				
			
			@ -352,8 +349,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 "Processing"
 | 
			
		||||
msgstr "Weiter"
 | 
			
		||||
| 
						 | 
				
			
			@ -383,13 +380,18 @@ msgstr "Bestellungsübersicht"
 | 
			
		|||
msgid "Product"
 | 
			
		||||
msgstr "Produkt"
 | 
			
		||||
 | 
			
		||||
#, python-format
 | 
			
		||||
msgid "Subtotal"
 | 
			
		||||
msgstr "Zwischensumme"
 | 
			
		||||
 | 
			
		||||
msgid "VAT"
 | 
			
		||||
msgstr "Mehrwertsteuer"
 | 
			
		||||
 | 
			
		||||
msgid ""
 | 
			
		||||
"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 ""
 | 
			
		||||
"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(vm_price)sCHF "
 | 
			
		||||
"pro Monat belastet"
 | 
			
		||||
"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit "
 | 
			
		||||
"%(vm_total_price)s CHF pro Monat belastet"
 | 
			
		||||
 | 
			
		||||
msgid "Place order"
 | 
			
		||||
msgstr "Bestellen"
 | 
			
		||||
| 
						 | 
				
			
			@ -455,25 +457,25 @@ msgstr "Wir unterstützen die FOSS Community."
 | 
			
		|||
msgid ""
 | 
			
		||||
"Data Center Light is the child of free and open source software (FOSS) "
 | 
			
		||||
"movement. <br>We grew up with it, live by it, and believe in it.<br> The "
 | 
			
		||||
"more we work on our data center,<br> the more we contribute back to the FOSS"
 | 
			
		||||
" community."
 | 
			
		||||
"more we work on our data center,<br> the more we contribute back to the FOSS "
 | 
			
		||||
"community."
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Data Center Light ist ein Teil der Free und Opens Source Software (FOSS) "
 | 
			
		||||
"Bewegung.<br/> Wir sind damit gross geworden, leben damit und glauben "
 | 
			
		||||
"daran.<br/> Je weiter wir mit unserem Data Center Light vorankommen, desto "
 | 
			
		||||
"mehr können wir etwas an die FOSS Community zurückgeben."
 | 
			
		||||
"Bewegung.<br/> Wir sind damit gross geworden, leben damit und glauben daran."
 | 
			
		||||
"<br/> Je weiter wir mit unserem Data Center Light vorankommen, desto mehr "
 | 
			
		||||
"können wir etwas an die FOSS Community zurückgeben."
 | 
			
		||||
 | 
			
		||||
msgid "We bring the future to you."
 | 
			
		||||
msgstr "Wir bringen die Zukunft zu dir."
 | 
			
		||||
 | 
			
		||||
msgid ""
 | 
			
		||||
"Data Center Light uses the most modern technologies out there.<br>Your VM "
 | 
			
		||||
"needs only IPv6. Data Center Light provides<br> transparent two-way "
 | 
			
		||||
"IPv6/IPv4 translation."
 | 
			
		||||
"needs only IPv6. Data Center Light provides<br> transparent two-way IPv6/"
 | 
			
		||||
"IPv4 translation."
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Data Center Light verwendet die zur Zeit modernsten Technologien.<br/>Deine "
 | 
			
		||||
"VM läuft mit IPv6. Data Center Light bietet eine transparente "
 | 
			
		||||
"IPv6/IPv4-Zweiweglösung."
 | 
			
		||||
"VM läuft mit IPv6. Data Center Light bietet eine transparente IPv6/IPv4-"
 | 
			
		||||
"Zweiweglösung."
 | 
			
		||||
 | 
			
		||||
msgid ""
 | 
			
		||||
" 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"
 | 
			
		||||
msgstr "Ungültige Speicher-Grösse"
 | 
			
		||||
 | 
			
		||||
#, python-brace-format
 | 
			
		||||
msgid "Incorrect pricing name. Please contact support{support_email}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
msgid "Confirm Order"
 | 
			
		||||
msgstr "Bestellung Bestätigen"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -507,8 +513,8 @@ msgid ""
 | 
			
		|||
"There was a payment related error. On close of this popup, you will be "
 | 
			
		||||
"redirected back to the payment page."
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Es ist ein Fehler bei der Zahlung betreten. Du wirst nach dem Schliessen vom"
 | 
			
		||||
" Popup zur Bezahlseite weitergeleitet."
 | 
			
		||||
"Es ist ein Fehler bei der Zahlung betreten. Du wirst nach dem Schliessen vom "
 | 
			
		||||
"Popup zur Bezahlseite weitergeleitet."
 | 
			
		||||
 | 
			
		||||
msgid "Thank you for the order."
 | 
			
		||||
msgstr "Danke für Deine Bestellung."
 | 
			
		||||
| 
						 | 
				
			
			@ -517,8 +523,14 @@ msgid ""
 | 
			
		|||
"Your VM will be up and running in a few moments. We will send you a "
 | 
			
		||||
"confirmation email as soon as it is ready."
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du"
 | 
			
		||||
" auf sie zugreifen kannst."
 | 
			
		||||
"Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du "
 | 
			
		||||
"auf sie zugreifen kannst."
 | 
			
		||||
 | 
			
		||||
#~ msgid "Pricing"
 | 
			
		||||
#~ msgstr "Preise"
 | 
			
		||||
 | 
			
		||||
#~ msgid "Order VM"
 | 
			
		||||
#~ msgstr "VM bestellen"
 | 
			
		||||
 | 
			
		||||
#~ msgid "Enter name"
 | 
			
		||||
#~ msgstr "Name"
 | 
			
		||||
| 
						 | 
				
			
			@ -533,18 +545,19 @@ msgstr ""
 | 
			
		|||
#~ msgstr "Anfrage verschickt"
 | 
			
		||||
 | 
			
		||||
#~ msgid ""
 | 
			
		||||
#~ "Thank you for your subscription! You will receive a confirmation mail from "
 | 
			
		||||
#~ "our team"
 | 
			
		||||
#~ "Thank you for your subscription! You will receive a confirmation mail "
 | 
			
		||||
#~ "from our team"
 | 
			
		||||
#~ msgstr ""
 | 
			
		||||
#~ "Vielen dank für Ihre Anmeldung. Sie erhalten in kürze eine Bestätigungsmail "
 | 
			
		||||
#~ "von unserem Team"
 | 
			
		||||
#~ "Vielen dank für Ihre Anmeldung. Sie erhalten in kürze eine "
 | 
			
		||||
#~ "Bestätigungsmail von unserem Team"
 | 
			
		||||
 | 
			
		||||
#~ msgid "Thank you for your request."
 | 
			
		||||
#~ msgstr "Vielen Dank für Deine Anfrage."
 | 
			
		||||
 | 
			
		||||
#~ msgid "You are one step away from being our beta tester!"
 | 
			
		||||
#~ 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 ""
 | 
			
		||||
#~ "Currently we are running our tests to make sure everything runs perfectly."
 | 
			
		||||
| 
						 | 
				
			
			@ -553,8 +566,8 @@ msgstr ""
 | 
			
		|||
#~ "sicherzustellen."
 | 
			
		||||
 | 
			
		||||
#~ msgid ""
 | 
			
		||||
#~ "In the meantime, we would like to ask you a little patience<br/> until our "
 | 
			
		||||
#~ "team contacts you with beta access."
 | 
			
		||||
#~ "In the meantime, we would like to ask you a little patience<br/> until "
 | 
			
		||||
#~ "our team contacts you with beta access."
 | 
			
		||||
#~ msgstr ""
 | 
			
		||||
#~ "Wir werden dann sobald als möglich Ihren Beta-Zugang erstellen und Sie "
 | 
			
		||||
#~ "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"
 | 
			
		||||
#~ msgstr ""
 | 
			
		||||
#~ "Vielen Dank für die Bestellung. Unser Team setzt sich sobald wie möglich mit"
 | 
			
		||||
#~ " Dir via E-Mail in Verbindung."
 | 
			
		||||
#~ "Vielen Dank für die Bestellung. Unser Team setzt sich sobald wie möglich "
 | 
			
		||||
#~ "mit Dir via E-Mail in Verbindung."
 | 
			
		||||
 | 
			
		||||
#~ msgid "Affordable VM hosting based in Switzerland"
 | 
			
		||||
#~ msgstr "Bezahlbares VM Hosting in der Schweiz"
 | 
			
		||||
| 
						 | 
				
			
			@ -581,18 +594,18 @@ msgstr ""
 | 
			
		|||
 | 
			
		||||
#~ msgid ""
 | 
			
		||||
#~ "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 "
 | 
			
		||||
#~ "our website, or if your order is more detailed, or if you encounter any "
 | 
			
		||||
#~ "technical hiccups, please contact us at support@datacenterlight.ch, our team"
 | 
			
		||||
#~ " will get in touch with you asap."
 | 
			
		||||
#~ "running in BETA mode. If you want more information that you did not find "
 | 
			
		||||
#~ "on our website, or if your order is more detailed, or if you encounter "
 | 
			
		||||
#~ "any technical hiccups, please contact us at support@datacenterlight.ch, "
 | 
			
		||||
#~ "our team will get in touch with you asap."
 | 
			
		||||
#~ msgstr ""
 | 
			
		||||
#~ "Unsere VMs werden in der Schweiz im Kanton Glarus gehostet und befinden sich"
 | 
			
		||||
#~ " zur Zeit noch in der BETA-Phase. Möchtest du mehr über uns erfahren und "
 | 
			
		||||
#~ "hast auf unserer Website nicht genügend Informationen gefunden? Möchtest "
 | 
			
		||||
#~ "eine detailliertere Bestellung aufgeben? Bist du auf technische Probleme "
 | 
			
		||||
#~ "gestossen, die du uns mitteilen möchtest? Dann zögere nicht und kontaktiere "
 | 
			
		||||
#~ "uns unter support@datacenterlight.ch. Unser Team wird sich umgehend um dein "
 | 
			
		||||
#~ "Anliegen kümmern!"
 | 
			
		||||
#~ "Unsere VMs werden in der Schweiz im Kanton Glarus gehostet und befinden "
 | 
			
		||||
#~ "sich zur Zeit noch in der BETA-Phase. Möchtest du mehr über uns erfahren "
 | 
			
		||||
#~ "und hast auf unserer Website nicht genügend Informationen gefunden? "
 | 
			
		||||
#~ "Möchtest eine detailliertere Bestellung aufgeben? Bist du auf technische "
 | 
			
		||||
#~ "Probleme gestossen, die du uns mitteilen möchtest? Dann zögere nicht und "
 | 
			
		||||
#~ "kontaktiere uns unter support@datacenterlight.ch. Unser Team wird sich "
 | 
			
		||||
#~ "umgehend um dein Anliegen kümmern!"
 | 
			
		||||
 | 
			
		||||
#~ msgid "is not a proper name"
 | 
			
		||||
#~ msgstr "ist kein gültiger Name"
 | 
			
		||||
| 
						 | 
				
			
			@ -610,12 +623,14 @@ msgstr ""
 | 
			
		|||
#~ "\n"
 | 
			
		||||
#~ "Hi,\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 ""
 | 
			
		||||
#~ "\n"
 | 
			
		||||
#~ "Hallo,\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"
 | 
			
		||||
#~ msgstr "Dein"
 | 
			
		||||
| 
						 | 
				
			
			@ -650,12 +665,14 @@ msgstr ""
 | 
			
		|||
#~ msgid "I want to have it!"
 | 
			
		||||
#~ 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 ""
 | 
			
		||||
#~ "Nachhaltigkeit: Wiederverwendung ehemaliger Fabrikhallen an Stelle der "
 | 
			
		||||
#~ "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 ""
 | 
			
		||||
#~ "Kreativität: Verwendung eines modernen und alternativen Designs für unser "
 | 
			
		||||
#~ "Datencenter"
 | 
			
		||||
| 
						 | 
				
			
			@ -678,8 +695,8 @@ msgstr ""
 | 
			
		|||
#~ msgstr "Standort des Datacenters ist in der Schweiz"
 | 
			
		||||
 | 
			
		||||
#~ msgid ""
 | 
			
		||||
#~ " WARNING: We are currently running in BETA mode. We hope you won't encounter"
 | 
			
		||||
#~ " any hiccups, but if you do, please let us know at "
 | 
			
		||||
#~ " WARNING: We are currently running in BETA mode. We hope you won't "
 | 
			
		||||
#~ "encounter any hiccups, but if you do, please let us know at "
 | 
			
		||||
#~ "support@datacenterlight.ch"
 | 
			
		||||
#~ msgstr ""
 | 
			
		||||
#~ " Achtung: Wir befinden uns zurzeit im Beta-Release. Wir hoffen, dass Sie "
 | 
			
		||||
| 
						 | 
				
			
			@ -693,8 +710,8 @@ msgstr ""
 | 
			
		|||
#~ msgstr "Unser Versprechen"
 | 
			
		||||
 | 
			
		||||
#~ msgid ""
 | 
			
		||||
#~ "Instead of creating an expensive SLA for availability, we promise that we do"
 | 
			
		||||
#~ " our best to run things as smooth as possible."
 | 
			
		||||
#~ "Instead of creating an expensive SLA for availability, we promise that we "
 | 
			
		||||
#~ "do our best to run things as smooth as possible."
 | 
			
		||||
#~ msgstr ""
 | 
			
		||||
#~ "Anstatt eines SLAs (Service Levle Agreements) zu vereinbaren,setzen wir "
 | 
			
		||||
#~ "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
 | 
			
		||||
 | 
			
		||||
logger = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VMTemplate(models.Model):
 | 
			
		||||
    name = models.CharField(max_length=50)
 | 
			
		||||
| 
						 | 
				
			
			@ -12,6 +16,59 @@ class VMTemplate(models.Model):
 | 
			
		|||
        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):
 | 
			
		||||
    """
 | 
			
		||||
    A model to store Data Center Light's created Stripe plans
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -120,6 +120,11 @@
 | 
			
		|||
    .header_slider .intro-cap {
 | 
			
		||||
        font-size: 3.25em;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .header_slider > .carousel .item .container {
 | 
			
		||||
        padding-left: 0;
 | 
			
		||||
        padding-right: 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.header_slider .intro_lead {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1231,6 +1231,15 @@ footer {
 | 
			
		|||
  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 {
 | 
			
		||||
  font-weight: 700;
 | 
			
		||||
  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() {
 | 
			
		||||
        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").text(total);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,6 +19,8 @@ from utils.forms import UserBillingAddressForm
 | 
			
		|||
from utils.mailer import BaseEmail
 | 
			
		||||
from utils.models import BillingAddress
 | 
			
		||||
 | 
			
		||||
from .models import VMPricing
 | 
			
		||||
 | 
			
		||||
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))
 | 
			
		||||
    vm_id = None
 | 
			
		||||
    try:
 | 
			
		||||
        final_price = specs.get('price')
 | 
			
		||||
        final_price = (specs.get('total_price') if 'total_price' in specs
 | 
			
		||||
                       else specs.get('price'))
 | 
			
		||||
        billing_address = BillingAddress(
 | 
			
		||||
            cardholder_name=billing_address_data['cardholder_name'],
 | 
			
		||||
            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:
 | 
			
		||||
            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
 | 
			
		||||
        order = HostingOrder.create(
 | 
			
		||||
            price=final_price,
 | 
			
		||||
            vm_id=vm_id,
 | 
			
		||||
            customer=customer,
 | 
			
		||||
            billing_address=billing_address
 | 
			
		||||
            billing_address=billing_address,
 | 
			
		||||
            vm_pricing=vm_pricing
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # Create a Hosting Bill
 | 
			
		||||
        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
 | 
			
		||||
        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'),
 | 
			
		||||
            'memory': specs.get('memory'),
 | 
			
		||||
            'storage': specs.get('disk_size'),
 | 
			
		||||
            'price': specs.get('price'),
 | 
			
		||||
            'price': final_price,
 | 
			
		||||
            'template': template.get('name'),
 | 
			
		||||
            'vm_name': vm.get('name'),
 | 
			
		||||
            'vm_id': vm['vm_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 = {
 | 
			
		||||
            'subject': settings.DCL_TEXT + " Order from %s" % context['email'],
 | 
			
		||||
            'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,7 @@
 | 
			
		|||
    <table style="width: 100%; border-spacing: 0; border-collapse: collapse; max-width: 560px;">
 | 
			
		||||
        <tr>
 | 
			
		||||
            <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>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,7 @@
 | 
			
		|||
    <table style="width: 100%; border-spacing: 0; border-collapse: collapse; max-width: 560px;">
 | 
			
		||||
        <tr>
 | 
			
		||||
            <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>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,16 @@
 | 
			
		|||
{% 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">
 | 
			
		||||
    {% csrf_token %}
 | 
			
		||||
    <div class="title">
 | 
			
		||||
| 
						 | 
				
			
			@ -7,9 +19,11 @@
 | 
			
		|||
    <div class="price">
 | 
			
		||||
        <span id="total">15</span>
 | 
			
		||||
        <span>CHF/{% trans "month" %}</span>
 | 
			
		||||
        {% if vm_pricing.vat_inclusive %}
 | 
			
		||||
        <div class="price-text">
 | 
			
		||||
            <p>{% trans "VAT included" %}</p>
 | 
			
		||||
        </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="descriptions">
 | 
			
		||||
        <div class="description form-group">
 | 
			
		||||
| 
						 | 
				
			
			@ -78,5 +92,6 @@
 | 
			
		|||
            </select>
 | 
			
		||||
        </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>
 | 
			
		||||
</form>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -78,7 +78,7 @@
 | 
			
		|||
                        <hr>
 | 
			
		||||
                        <p>{% trans "Configuration"%} <strong class="pull-right">{{request.session.template.name}}</strong></p>
 | 
			
		||||
                        <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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,9 +65,19 @@
 | 
			
		|||
                            <span>{% trans "Disk space" %}: </span>
 | 
			
		||||
                            <span class="pull-right">{{vm.disk_size|intcomma}} GB</span>
 | 
			
		||||
                        </p>
 | 
			
		||||
                        {% if vm.vat > 0 %}
 | 
			
		||||
                            <p>
 | 
			
		||||
                                <strong>{% trans "Subtotal" %}: </strong>
 | 
			
		||||
                                <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>
 | 
			
		||||
                            <span>{% trans "Total" %}</span>
 | 
			
		||||
                            <span class="pull-right">{{vm.price|intcomma}} CHF</span>
 | 
			
		||||
                            <strong>{% trans "Total" %}</strong>
 | 
			
		||||
                            <span class="pull-right">{{vm.total_price|floatformat:2|intcomma}} CHF</span>
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +88,7 @@
 | 
			
		|||
            {% csrf_token %}
 | 
			
		||||
            <div class="row">
 | 
			
		||||
                <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 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">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,11 +19,11 @@ from hosting.models import HostingOrder
 | 
			
		|||
from membership.models import CustomUser, StripeCustomer
 | 
			
		||||
from opennebula_api.serializers import VMTemplateSerializer
 | 
			
		||||
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.tasks import send_plain_email_task
 | 
			
		||||
from .forms import ContactForm
 | 
			
		||||
from .models import VMTemplate
 | 
			
		||||
from .models import VMTemplate, VMPricing
 | 
			
		||||
from .utils import get_cms_integration
 | 
			
		||||
 | 
			
		||||
logger = logging.getLogger(__name__)
 | 
			
		||||
| 
						 | 
				
			
			@ -93,7 +93,8 @@ class IndexView(CreateView):
 | 
			
		|||
 | 
			
		||||
    @cache_control(no_cache=True, must_revalidate=True, no_store=True)
 | 
			
		||||
    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:
 | 
			
		||||
                del request.session[session_var]
 | 
			
		||||
        return HttpResponseRedirect(reverse('datacenterlight:cms_index'))
 | 
			
		||||
| 
						 | 
				
			
			@ -106,12 +107,30 @@ class IndexView(CreateView):
 | 
			
		|||
        storage = request.POST.get('storage')
 | 
			
		||||
        storage_field = forms.IntegerField(validators=[self.validate_storage])
 | 
			
		||||
        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(
 | 
			
		||||
            opennebula_vm_template_id=template_id
 | 
			
		||||
        ).first()
 | 
			
		||||
        template_data = VMTemplateSerializer(template).data
 | 
			
		||||
        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:
 | 
			
		||||
            cores = cores_field.clean(cores)
 | 
			
		||||
        except ValidationError as err:
 | 
			
		||||
| 
						 | 
				
			
			@ -139,14 +158,21 @@ class IndexView(CreateView):
 | 
			
		|||
            )
 | 
			
		||||
            return HttpResponseRedirect(referer_url + "#order_form")
 | 
			
		||||
 | 
			
		||||
        amount_to_be_charged = get_vm_price(
 | 
			
		||||
            cpu=cores, memory=memory, disk_size=storage
 | 
			
		||||
        price, vat, vat_percent = get_vm_price_with_vat(
 | 
			
		||||
            cpu=cores,
 | 
			
		||||
            memory=memory,
 | 
			
		||||
            ssd_size=storage,
 | 
			
		||||
            pricing_name=vm_pricing_name
 | 
			
		||||
        )
 | 
			
		||||
        specs = {
 | 
			
		||||
            'cpu': cores,
 | 
			
		||||
            'memory': memory,
 | 
			
		||||
            '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['template'] = template_data
 | 
			
		||||
| 
						 | 
				
			
			@ -220,7 +246,10 @@ class PaymentOrderView(FormView):
 | 
			
		|||
            'site_url': reverse('datacenterlight:index'),
 | 
			
		||||
            'login_form': HostingUserLoginForm(prefix='login_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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -393,7 +422,7 @@ class OrderConfirmationView(DetailView):
 | 
			
		|||
        cpu = specs.get('cpu')
 | 
			
		||||
        memory = specs.get('memory')
 | 
			
		||||
        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,
 | 
			
		||||
                                                     memory=memory,
 | 
			
		||||
                                                     disk_size=disk_size)
 | 
			
		||||
| 
						 | 
				
			
			@ -489,7 +518,7 @@ class OrderConfirmationView(DetailView):
 | 
			
		|||
                             stripe_subscription_obj.id, card_details_dict)
 | 
			
		||||
        for session_var in ['specs', 'template', 'billing_address',
 | 
			
		||||
                            'billing_address_data',
 | 
			
		||||
                            'token', 'customer']:
 | 
			
		||||
                            'token', 'customer', 'pricing_name']:
 | 
			
		||||
            if session_var in request.session:
 | 
			
		||||
                del request.session[session_var]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -67,6 +67,7 @@ urlpatterns += i18n_patterns(
 | 
			
		|||
        include('ungleich_page.urls',
 | 
			
		||||
                namespace='ungleich_page'),
 | 
			
		||||
        name='ungleich_page'),
 | 
			
		||||
    url(r'^blog/', include('djangocms_blog.urls', namespace='djangocms_blog')),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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.functional import cached_property
 | 
			
		||||
from Crypto.PublicKey import RSA
 | 
			
		||||
 | 
			
		||||
from datacenterlight.models import VMPricing
 | 
			
		||||
from membership.models import StripeCustomer, CustomUser
 | 
			
		||||
from utils.models import BillingAddress
 | 
			
		||||
from utils.mixins import AssignPermissionsMixin
 | 
			
		||||
| 
						 | 
				
			
			@ -53,6 +55,7 @@ class HostingOrder(AssignPermissionsMixin, models.Model):
 | 
			
		|||
    stripe_charge_id = models.CharField(max_length=100, null=True)
 | 
			
		||||
    price = models.FloatField()
 | 
			
		||||
    subscription_id = models.CharField(max_length=100, null=True)
 | 
			
		||||
    vm_pricing = models.ForeignKey(VMPricing)
 | 
			
		||||
 | 
			
		||||
    permissions = ('view_hostingorder',)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -70,12 +73,13 @@ class HostingOrder(AssignPermissionsMixin, models.Model):
 | 
			
		|||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def create(cls, price=None, vm_id=None, customer=None,
 | 
			
		||||
               billing_address=None):
 | 
			
		||||
               billing_address=None, vm_pricing=None):
 | 
			
		||||
        instance = cls.objects.create(
 | 
			
		||||
            price=price,
 | 
			
		||||
            vm_id=vm_id,
 | 
			
		||||
            customer=customer,
 | 
			
		||||
            billing_address=billing_address
 | 
			
		||||
            billing_address=billing_address,
 | 
			
		||||
            vm_pricing=vm_pricing
 | 
			
		||||
        )
 | 
			
		||||
        instance.assign_permissions(customer.user)
 | 
			
		||||
        return instance
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,7 @@
 | 
			
		|||
    <table style="width: 100%; border-spacing: 0; border-collapse: collapse; max-width: 560px;">
 | 
			
		||||
        <tr>
 | 
			
		||||
            <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>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,7 @@
 | 
			
		|||
    <table style="width: 100%; border-spacing: 0; border-collapse: collapse; max-width: 560px;">
 | 
			
		||||
        <tr>
 | 
			
		||||
            <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>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,7 @@
 | 
			
		|||
    <table style="width: 100%; border-spacing: 0; border-collapse: collapse; max-width: 560px;">
 | 
			
		||||
        <tr>
 | 
			
		||||
            <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>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -74,7 +74,7 @@
 | 
			
		|||
              <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;">
 | 
			
		||||
<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 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>
 | 
			
		||||
| 
						 | 
				
			
			@ -100,7 +100,7 @@
 | 
			
		|||
          </tr>
 | 
			
		||||
<tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
 | 
			
		||||
<td class="free-text" style="border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; text-align: center; line-height: 21px; width: 100% !important; padding: 10px 60px 0px;" align="center">
 | 
			
		||||
             Your virtual machine {{vm.name}} subscription has been charged, <br/> you can view your invoice clicking on the button below. 
 | 
			
		||||
             Your virtual machine {{vm.name}} subscription has been charged, <br/> you can view your invoice clicking on the button below.
 | 
			
		||||
            </td>
 | 
			
		||||
          </tr>
 | 
			
		||||
<tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -127,9 +127,19 @@
 | 
			
		|||
                            <span>{% trans "Disk space" %}: </span>
 | 
			
		||||
                            <span class="pull-right">{{vm.disk_size}} GB</span>
 | 
			
		||||
                        </p>
 | 
			
		||||
                        {% if vm.vat > 0 %}
 | 
			
		||||
                            <p>
 | 
			
		||||
                                <strong>{% trans "Subtotal" %}: </strong>
 | 
			
		||||
                                <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>
 | 
			
		||||
                            <span>{% trans "Total" %}</span>
 | 
			
		||||
                            <span class="pull-right">{{vm.price|intcomma}} CHF</span>
 | 
			
		||||
                            <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>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,7 +29,7 @@
 | 
			
		|||
                <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-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">
 | 
			
		||||
                        <a class="btn btn-order-detail" href="{% url 'hosting:orders' order.pk %}">{% trans 'See Invoice' %}</a>
 | 
			
		||||
                    </td>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,7 +45,7 @@
 | 
			
		|||
				<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-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>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,7 +42,7 @@ from utils.forms import (
 | 
			
		|||
    BillingAddressForm, PasswordResetRequestForm, UserBillingAddressForm,
 | 
			
		||||
    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.stripe_utils import StripeUtils
 | 
			
		||||
from utils.tasks import send_plain_email_task
 | 
			
		||||
| 
						 | 
				
			
			@ -749,11 +749,17 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView):
 | 
			
		|||
                context['vm'] = vm_detail.__dict__
 | 
			
		||||
                context['vm']['name'] = '{}-{}'.format(
 | 
			
		||||
                    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'],
 | 
			
		||||
                    disk_size=context['vm']['disk_size'],
 | 
			
		||||
                    memory=context['vm']['memory']
 | 
			
		||||
                    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
 | 
			
		||||
                context['subscription_end_date'] = vm_detail.end_date()
 | 
			
		||||
            except VMDetail.DoesNotExist:
 | 
			
		||||
                try:
 | 
			
		||||
| 
						 | 
				
			
			@ -762,6 +768,17 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView):
 | 
			
		|||
                    )
 | 
			
		||||
                    vm = manager.get_vm(obj.vm_id)
 | 
			
		||||
                    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:
 | 
			
		||||
                    messages.error(
 | 
			
		||||
                        self.request,
 | 
			
		||||
| 
						 | 
				
			
			@ -1100,7 +1117,8 @@ class VirtualMachineView(LoginRequiredMixin, View):
 | 
			
		|||
            context = {
 | 
			
		||||
                'virtual_machine': serializer.data,
 | 
			
		||||
                'order': HostingOrder.objects.get(
 | 
			
		||||
                    vm_id=serializer.data['vm_id'])
 | 
			
		||||
                    vm_id=serializer.data['vm_id']
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        except Exception as ex:
 | 
			
		||||
            logger.debug("Exception generated {}".format(str(ex)))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,8 @@
 | 
			
		|||
import decimal
 | 
			
		||||
import logging
 | 
			
		||||
from oca.pool import WrongIdError
 | 
			
		||||
 | 
			
		||||
from datacenterlight.models import VMPricing
 | 
			
		||||
from hosting.models import UserHostingKey, VMDetail
 | 
			
		||||
from opennebula_api.serializers import VirtualMachineSerializer
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -49,14 +51,74 @@ def get_or_create_vm_detail(user, manager, vm_id):
 | 
			
		|||
    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
 | 
			
		||||
    ssd parameters
 | 
			
		||||
 | 
			
		||||
    :param cpu: Number of cores 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 (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…
	
	Add table
		Add a link
		
	
		Reference in a new issue