merge master
This commit is contained in:
		
				commit
				
					
						b660ac5ed4
					
				
			
		
					 31 changed files with 529 additions and 125 deletions
				
			
		
							
								
								
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -41,4 +41,5 @@ secret-key | ||||||
| /utils/optimize/ | /utils/optimize/ | ||||||
| 
 | 
 | ||||||
| # 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 | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| from django.contrib import admin | from django.contrib import admin | ||||||
| from cms.admin.placeholderadmin import PlaceholderAdminMixin | from cms.admin.placeholderadmin import PlaceholderAdminMixin | ||||||
| from .cms_models import CMSIntegration | from .cms_models import CMSIntegration | ||||||
|  | from .models import VMPricing | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class CMSIntegrationAdmin(PlaceholderAdminMixin, admin.ModelAdmin): | class CMSIntegrationAdmin(PlaceholderAdminMixin, admin.ModelAdmin): | ||||||
|  | @ -8,3 +9,4 @@ class CMSIntegrationAdmin(PlaceholderAdminMixin, admin.ModelAdmin): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| admin.site.register(CMSIntegration, CMSIntegrationAdmin) | admin.site.register(CMSIntegration, CMSIntegrationAdmin) | ||||||
|  | admin.site.register(VMPricing) | ||||||
|  |  | ||||||
|  | @ -6,6 +6,8 @@ from django.utils.safestring import mark_safe | ||||||
| from djangocms_text_ckeditor.fields import HTMLField | from djangocms_text_ckeditor.fields import HTMLField | ||||||
| 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( | ||||||
|  | @ -275,3 +277,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!" | ||||||
| 
 | 
 | ||||||
|  | @ -203,8 +192,8 @@ msgid "" | ||||||
| "order to make it more sustainable and affordable at the same time." | "order to make it more sustainable and affordable at the same time." | ||||||
| msgstr "" | msgstr "" | ||||||
| "Ist kreativ, indem es sich ein modernes und alternatives Layout zu Nutze " | "Ist kreativ, indem es sich ein modernes und alternatives Layout zu Nutze " | ||||||
| "macht um Nachhaltigkeit zu fördern und somit erschwingliche Preise bieten zu" | "macht um Nachhaltigkeit zu fördern und somit erschwingliche Preise bieten zu " | ||||||
| " können.
" | "können.
" | ||||||
| 
 | 
 | ||||||
| msgid "" | msgid "" | ||||||
| "Cuts down the costs for you by using FOSS (Free Open Source Software) " | "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 " | "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,20 +318,20 @@ 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, " | ||||||
| "you will be taken to the Confirm Order Page." | "you will be taken to the Confirm Order Page." | ||||||
| msgstr "" | msgstr "" | ||||||
| "Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst ausgelöst," | "Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst ausgelöst, " | ||||||
| " nachdem Du die Bestellung auf der nächsten Seite bestätigt hast." | "nachdem Du die Bestellung auf der nächsten Seite bestätigt hast." | ||||||
| 
 | 
 | ||||||
| msgid "Card Number" | msgid "Card Number" | ||||||
| msgstr "Kreditkartennummer" | msgstr "Kreditkartennummer" | ||||||
|  | @ -352,8 +349,8 @@ msgid "" | ||||||
| "You are not making any payment yet. After placing your order, you will be " | "You are not making any payment yet. After placing your order, you will be " | ||||||
| "taken to the Submit Payment Page." | "taken to the Submit Payment Page." | ||||||
| msgstr "" | msgstr "" | ||||||
| "Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst ausgelöst," | "Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst ausgelöst, " | ||||||
| " nachdem Du die Bestellung auf der nächsten Seite bestätigt hast." | "nachdem Du die Bestellung auf der nächsten Seite bestätigt hast." | ||||||
| 
 | 
 | ||||||
| msgid "Processing" | msgid "Processing" | ||||||
| msgstr "Weiter" | msgstr "Weiter" | ||||||
|  | @ -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" | ||||||
|  | @ -455,25 +457,25 @@ msgstr "Wir unterstützen die FOSS Community." | ||||||
| msgid "" | msgid "" | ||||||
| "Data Center Light is the child of free and open source software (FOSS) " | "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 " | "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" | "more we work on our data center,<br> the more we contribute back to the FOSS " | ||||||
| " 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" | ||||||
| 
 | 
 | ||||||
|  | @ -507,8 +513,8 @@ msgid "" | ||||||
| "There was a payment related error. On close of this popup, you will be " | "There was a payment related error. On close of this popup, you will be " | ||||||
| "redirected back to the payment page." | "redirected back to the payment page." | ||||||
| msgstr "" | msgstr "" | ||||||
| "Es ist ein Fehler bei der Zahlung betreten. Du wirst nach dem Schliessen vom" | "Es ist ein Fehler bei der Zahlung betreten. Du wirst nach dem Schliessen vom " | ||||||
| " Popup zur Bezahlseite weitergeleitet." | "Popup zur Bezahlseite weitergeleitet." | ||||||
| 
 | 
 | ||||||
| msgid "Thank you for the order." | msgid "Thank you for the order." | ||||||
| msgstr "Danke für Deine Bestellung." | 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 " | "Your VM will be up and running in a few moments. We will send you a " | ||||||
| "confirmation email as soon as it is ready." | "confirmation email as soon as it is ready." | ||||||
| msgstr "" | 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> | ||||||
|  |                                 <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> |                         <p> | ||||||
|                             <span>{% trans "Total" %}</span> |                             <strong>{% trans "Total" %}</strong> | ||||||
|                             <span class="pull-right">{{vm.price|intcomma}} CHF</span> |                             <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] | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										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> | ||||||
|  | @ -100,7 +100,7 @@ | ||||||
|           </tr> |           </tr> | ||||||
| <tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;"> | <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"> | <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> |             </td> | ||||||
|           </tr> |           </tr> | ||||||
| <tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;"> | <tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;"> | ||||||
|  |  | ||||||
|  | @ -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> | ||||||
|  |                                 <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> |                         <p> | ||||||
|                             <span>{% trans "Total" %}</span> |                             <strong>{% trans "Total" %}</strong> | ||||||
|                             <span class="pull-right">{{vm.price|intcomma}} CHF</span> |                             <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…
	
	Add table
		Add a link
		
	
		Reference in a new issue