merged master
This commit is contained in:
		
				commit
				
					
						a5bd8347e8
					
				
			
		
					 32 changed files with 654 additions and 165 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -42,3 +42,4 @@ secret-key | ||||||
| 
 | 
 | ||||||
| # to keep empty dirs | # to keep empty dirs | ||||||
| !.gitkeep | !.gitkeep | ||||||
|  | *.orig | ||||||
|  |  | ||||||
|  | @ -1,7 +1,10 @@ | ||||||
| next: | 1.7: 2018-04-20 | ||||||
|     * bgfix: [all] Make /blog available on all domains |     * bgfix: [all] Make /blog available on all domains | ||||||
|     * #4367: [dcl] email logo resolution fix |     * #4367: [dcl] email logo resolution fix | ||||||
|     * #4376: [cms] dcl promo section plugin link color changed to brighter shade |     * #4376: [cms] dcl promo section plugin link color changed to brighter shade | ||||||
|  |     * #4379: [dcl] pricing without VAT | ||||||
|  |     * bgfix: [blog] fix top menu items to show only one item | ||||||
|  |     * #4297: [cms] favicon as a page attribute for dcl template | ||||||
| 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,10 +1,18 @@ | ||||||
| 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.extensions import PageExtensionAdmin | ||||||
|  | from .cms_models import CMSIntegration, CMSFaviconExtension | ||||||
|  | from .models import VMPricing | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class CMSIntegrationAdmin(PlaceholderAdminMixin, admin.ModelAdmin): | class CMSIntegrationAdmin(PlaceholderAdminMixin, admin.ModelAdmin): | ||||||
|     list_display = ('name', 'domain') |     list_display = ('name', 'domain') | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class CMSFaviconExtensionAdmin(PageExtensionAdmin): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| admin.site.register(CMSIntegration, CMSIntegrationAdmin) | admin.site.register(CMSIntegration, CMSIntegrationAdmin) | ||||||
|  | admin.site.register(CMSFaviconExtension, CMSFaviconExtensionAdmin) | ||||||
|  | admin.site.register(VMPricing) | ||||||
|  |  | ||||||
|  | @ -1,11 +1,16 @@ | ||||||
|  | from cms.extensions import PageExtension | ||||||
|  | from cms.extensions.extension_pool import extension_pool | ||||||
| from cms.models.fields import PlaceholderField | from cms.models.fields import PlaceholderField | ||||||
| from cms.models.pluginmodel import CMSPlugin | from cms.models.pluginmodel import CMSPlugin | ||||||
| from django.contrib.sites.models import Site | from django.contrib.sites.models import Site | ||||||
| from django.db import models | from django.db import models | ||||||
| from django.utils.safestring import mark_safe | from django.utils.safestring import mark_safe | ||||||
| from djangocms_text_ckeditor.fields import HTMLField | from djangocms_text_ckeditor.fields import HTMLField | ||||||
|  | from filer.fields.file import FilerFileField | ||||||
| from filer.fields.image import FilerImageField | from filer.fields.image import FilerImageField | ||||||
| 
 | 
 | ||||||
|  | from datacenterlight.models import VMPricing | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class CMSIntegration(models.Model): | class CMSIntegration(models.Model): | ||||||
|     name = models.CharField( |     name = models.CharField( | ||||||
|  | @ -30,9 +35,15 @@ class CMSIntegration(models.Model): | ||||||
|         return self.name |         return self.name | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # Models for CMS Plugins | class CMSFaviconExtension(PageExtension): | ||||||
|  |     favicon = FilerFileField(related_name="cms_favicon_image") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | extension_pool.register(CMSFaviconExtension) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Models for CMS Plugins | ||||||
|  | 
 | ||||||
| class DCLSectionPluginModel(CMSPlugin): | class DCLSectionPluginModel(CMSPlugin): | ||||||
|     heading = models.CharField( |     heading = models.CharField( | ||||||
|         blank=True, null=True, max_length=100, |         blank=True, null=True, max_length=100, | ||||||
|  | @ -275,3 +286,12 @@ class DCLSectionPromoPluginModel(CMSPlugin): | ||||||
|         if self.background_image: |         if self.background_image: | ||||||
|             extra_classes += ' promo-with-bg' |             extra_classes += ' promo-with-bg' | ||||||
|         return extra_classes |         return extra_classes | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DCLCustomPricingModel(CMSPlugin): | ||||||
|  |     pricing = models.ForeignKey( | ||||||
|  |         VMPricing, | ||||||
|  |         related_name="dcl_custom_pricing_vm_pricing", | ||||||
|  |         help_text='Choose a pricing that will be associated with this ' | ||||||
|  |                   'Calculator' | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  | @ -6,9 +6,9 @@ from .cms_models import ( | ||||||
|     DCLFooterPluginModel, DCLLinkPluginModel, DCLNavbarDropdownPluginModel, |     DCLFooterPluginModel, DCLLinkPluginModel, DCLNavbarDropdownPluginModel, | ||||||
|     DCLSectionIconPluginModel, DCLSectionImagePluginModel, |     DCLSectionIconPluginModel, DCLSectionImagePluginModel, | ||||||
|     DCLSectionPluginModel, DCLNavbarPluginModel, |     DCLSectionPluginModel, DCLNavbarPluginModel, | ||||||
|     DCLSectionPromoPluginModel |     DCLSectionPromoPluginModel, DCLCustomPricingModel | ||||||
| ) | ) | ||||||
| from .models import VMTemplate | from .models import VMTemplate, VMPricing | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @plugin_pool.register_plugin | @plugin_pool.register_plugin | ||||||
|  | @ -75,13 +75,13 @@ class DCLSectionPromoPlugin(CMSPluginBase): | ||||||
| @plugin_pool.register_plugin | @plugin_pool.register_plugin | ||||||
| class DCLCalculatorPlugin(CMSPluginBase): | class DCLCalculatorPlugin(CMSPluginBase): | ||||||
|     module = "Datacenterlight" |     module = "Datacenterlight" | ||||||
|     name = "DCL Calculator Plugin" |     name = "DCL Calculator Section Plugin" | ||||||
|     model = DCLSectionPluginModel |     model = DCLSectionPluginModel | ||||||
|     render_template = "datacenterlight/cms/calculator.html" |     render_template = "datacenterlight/cms/calculator.html" | ||||||
|     cache = False |     cache = False | ||||||
|     allow_children = True |     allow_children = True | ||||||
|     child_classes = [ |     child_classes = [ | ||||||
|         'DCLSectionPromoPlugin', 'UngleichHTMLPlugin' |         'DCLSectionPromoPlugin', 'UngleichHTMLPlugin', 'DCLCustomPricingPlugin' | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     def render(self, context, instance, placeholder): |     def render(self, context, instance, placeholder): | ||||||
|  | @ -89,15 +89,38 @@ class DCLCalculatorPlugin(CMSPluginBase): | ||||||
|             context, instance, placeholder |             context, instance, placeholder | ||||||
|         ) |         ) | ||||||
|         context['templates'] = VMTemplate.objects.all() |         context['templates'] = VMTemplate.objects.all() | ||||||
|         context['children_to_side'] = [] |  | ||||||
|         context['children_to_content'] = [] |         context['children_to_content'] = [] | ||||||
|  |         pricing_plugin_model = None | ||||||
|         if instance.child_plugin_instances is not None: |         if instance.child_plugin_instances is not None: | ||||||
|             context['children_to_content'].extend( |             context['children_to_content'].extend( | ||||||
|                 instance.child_plugin_instances |                 instance.child_plugin_instances | ||||||
|             ) |             ) | ||||||
|  |             for child in instance.child_plugin_instances: | ||||||
|  |                 if child.__class__.__name__ == 'DCLCustomPricingModel': | ||||||
|  |                     # The second clause is just to make sure we pick up the | ||||||
|  |                     # most recent CustomPricing, if more than one is present | ||||||
|  |                     if (pricing_plugin_model is None or child.pricing_id > | ||||||
|  |                             pricing_plugin_model.model.pricing_id): | ||||||
|  |                         pricing_plugin_model = child | ||||||
|  | 
 | ||||||
|  |         if pricing_plugin_model: | ||||||
|  |             context['vm_pricing'] = VMPricing.get_vm_pricing_by_name( | ||||||
|  |                 name=pricing_plugin_model.pricing.name | ||||||
|  |             ) | ||||||
|  |         else: | ||||||
|  |             context['vm_pricing'] = VMPricing.get_default_pricing() | ||||||
|  | 
 | ||||||
|         return context |         return context | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @plugin_pool.register_plugin | ||||||
|  | class DCLCustomPricingPlugin(CMSPluginBase): | ||||||
|  |     module = "Datacenterlight" | ||||||
|  |     name = "DCL Custom Pricing Plugin" | ||||||
|  |     model = DCLCustomPricingModel | ||||||
|  |     render_plugin = False | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| @plugin_pool.register_plugin | @plugin_pool.register_plugin | ||||||
| class DCLBannerListPlugin(CMSPluginBase): | class DCLBannerListPlugin(CMSPluginBase): | ||||||
|     module = "Datacenterlight" |     module = "Datacenterlight" | ||||||
|  |  | ||||||
							
								
								
									
										24
									
								
								datacenterlight/cms_toolbar.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								datacenterlight/cms_toolbar.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | ||||||
|  | from cms.extensions.toolbar import ExtensionToolbar | ||||||
|  | from cms.toolbar_pool import toolbar_pool | ||||||
|  | from django.utils.translation import ugettext_lazy as _ | ||||||
|  | 
 | ||||||
|  | from .cms_models import CMSFaviconExtension | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @toolbar_pool.register | ||||||
|  | class CMSFaviconExtensionToolbar(ExtensionToolbar): | ||||||
|  |     # defineds the model for the current toolbar | ||||||
|  |     model = CMSFaviconExtension | ||||||
|  | 
 | ||||||
|  |     def populate(self): | ||||||
|  |         # setup the extension toolbar with permissions and sanity checks | ||||||
|  |         current_page_menu = self._setup_extension_toolbar() | ||||||
|  |         # if it's all ok | ||||||
|  |         if current_page_menu: | ||||||
|  |             # retrieves the instance of the current extension (if any) and the toolbar item url | ||||||
|  |             page_extension, url = self.get_page_extension_admin() | ||||||
|  |             if url: | ||||||
|  |                 # adds a toolbar item | ||||||
|  |                 current_page_menu.add_modal_item( | ||||||
|  |                     _('CMS Favicon'), url=url, disabled=not self.toolbar.edit_mode | ||||||
|  |                 ) | ||||||
|  | @ -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'), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
							
								
								
									
										29
									
								
								datacenterlight/migrations/0019_cmsfaviconextension.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								datacenterlight/migrations/0019_cmsfaviconextension.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Generated by Django 1.9.4 on 2018-04-12 03:16 | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
|  | from django.db import migrations, models | ||||||
|  | import django.db.models.deletion | ||||||
|  | import filer.fields.file | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('datacenterlight', '0018_auto_20180403_1930'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name='CMSFaviconExtension', | ||||||
|  |             fields=[ | ||||||
|  |                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||||||
|  |                 ('extended_object', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='cms.Page')), | ||||||
|  |                 ('favicon', filer.fields.file.FilerFileField(on_delete=django.db.models.deletion.CASCADE, related_name='cms_favicon_image', to='filer.File')), | ||||||
|  |                 ('public_extension', models.OneToOneField(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='draft_extension', to='datacenterlight.CMSFaviconExtension')), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 'abstract': False, | ||||||
|  |             }, | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
							
								
								
									
										16
									
								
								datacenterlight/migrations/0020_merge.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								datacenterlight/migrations/0020_merge.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Generated by Django 1.9.4 on 2018-04-20 15:04 | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
|  | from django.db import migrations | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('datacenterlight', '0019_auto_20180415_2236'), | ||||||
|  |         ('datacenterlight', '0019_cmsfaviconextension'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |     ] | ||||||
|  | @ -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 { | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -18,6 +18,8 @@ from utils.hosting_utils import get_all_public_keys, get_or_create_vm_detail | ||||||
| from utils.mailer import BaseEmail | from utils.mailer import BaseEmail | ||||||
| from utils.stripe_utils import StripeUtils | from utils.stripe_utils import StripeUtils | ||||||
| 
 | 
 | ||||||
|  | from .models import VMPricing | ||||||
|  | 
 | ||||||
| logger = get_task_logger(__name__) | logger = get_task_logger(__name__) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -53,6 +55,11 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): | ||||||
|         "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('total_price') if 'total_price' in specs | ||||||
|  |             else specs.get('price') | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|         if 'pass' in user: |         if 'pass' in user: | ||||||
|             on_user = user.get('email') |             on_user = user.get('email') | ||||||
|             on_pass = user.get('pass') |             on_pass = user.get('pass') | ||||||
|  | @ -110,8 +117,8 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): | ||||||
| 
 | 
 | ||||||
|         if result.get('error') is not None: |         if result.get('error') is not None: | ||||||
|             emsg = "Could not update subscription metadata for {sub}".format( |             emsg = "Could not update subscription metadata for {sub}".format( | ||||||
|                     sub=hosting_order.subscription_id |                 sub=hosting_order.subscription_id | ||||||
|                 ) |             ) | ||||||
|             logger.error(emsg) |             logger.error(emsg) | ||||||
|             if error_msg: |             if error_msg: | ||||||
|                 error_msg += emsg |                 error_msg += emsg | ||||||
|  | @ -126,7 +133,7 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): | ||||||
|             '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'], | ||||||
|  | @ -135,6 +142,10 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): | ||||||
| 
 | 
 | ||||||
|         if error_msg: |         if error_msg: | ||||||
|             context['errors'] = error_msg |             context['errors'] = error_msg | ||||||
|  |         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, | ||||||
|  |  | ||||||
|  | @ -8,9 +8,9 @@ | ||||||
|     <meta charset="utf-8"> |     <meta charset="utf-8"> | ||||||
|     <meta http-equiv="X-UA-Compatible" content="IE=edge"> |     <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> |     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||||
|     <meta name="description" content="Data Center Light by ungleich"> |     <meta name="description" content="{% page_attribute 'meta_description' %}"> | ||||||
|     <meta name="author" content="ungleich glarus ag"> |     <meta name="author" content="ungleich glarus ag"> | ||||||
|     <title>{% page_attribute page_title %}</title> |     <title>{% page_attribute "page_title" %}</title> | ||||||
| 
 | 
 | ||||||
|     <!-- Vendor CSS --> |     <!-- Vendor CSS --> | ||||||
|     <!-- Bootstrap Core CSS --> |     <!-- Bootstrap Core CSS --> | ||||||
|  | @ -30,7 +30,11 @@ | ||||||
|     <!-- External Fonts --> |     <!-- External Fonts --> | ||||||
|     <link href="//fonts.googleapis.com/css?family=Lato:300,400,600,700" rel="stylesheet" type="text/css"> |     <link href="//fonts.googleapis.com/css?family=Lato:300,400,600,700" rel="stylesheet" type="text/css"> | ||||||
| 
 | 
 | ||||||
|     <link rel="shortcut icon" href="{% static 'datacenterlight/img/favicon.ico' %}" type="image/x-icon"> |     {% if request.current_page.cmsfaviconextension %} | ||||||
|  |         <link rel="shortcut icon" href="{% static request.current_page.cmsfaviconextension.favicon.url %}" type="image/x-icon"> | ||||||
|  |     {% else %} | ||||||
|  |         <link rel="shortcut icon" href="{% static 'datacenterlight/img/favicon.ico' %}" type="image/x-icon"> | ||||||
|  |     {% endif %} | ||||||
| 
 | 
 | ||||||
|     <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> |     <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> | ||||||
|     <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> |     <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> | ||||||
|  | @ -52,7 +56,7 @@ | ||||||
|     {% placeholder 'Datacenterlight Header' or %} |     {% placeholder 'Datacenterlight Header' or %} | ||||||
|         <div class="dcl-header"> |         <div class="dcl-header"> | ||||||
|             <div class="container"> |             <div class="container"> | ||||||
|                 <h1>{% page_attribute page_title %}</h1> |                 <h1>{% page_attribute "page_title" %}</h1> | ||||||
|             </div> |             </div> | ||||||
|         </div> |         </div> | ||||||
|     {% endplaceholder %} |     {% endplaceholder %} | ||||||
|  |  | ||||||
|  | @ -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"> | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ from hosting.models import HostingOrder, HostingBill | ||||||
| from membership.models import StripeCustomer | from membership.models import StripeCustomer | ||||||
| from utils.forms import UserBillingAddressForm | from utils.forms import UserBillingAddressForm | ||||||
| from utils.models import BillingAddress | from utils.models import BillingAddress | ||||||
|  | from .models import VMPricing | ||||||
| from .cms_models import CMSIntegration | from .cms_models import CMSIntegration | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -30,16 +31,26 @@ def create_vm(billing_address_data, stripe_customer_id, specs, | ||||||
|         country=billing_address_data['country'] |         country=billing_address_data['country'] | ||||||
|     ) |     ) | ||||||
|     billing_address.save() |     billing_address.save() | ||||||
| 
 |  | ||||||
|     customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() |     customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() | ||||||
|  |     vm_pricing = ( | ||||||
|  |         VMPricing.get_vm_pricing_by_name(name=specs['pricing_name']) | ||||||
|  |         if 'pricing_name' in specs else | ||||||
|  |         VMPricing.get_default_pricing() | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     final_price = ( | ||||||
|  |         specs.get('total_price') | ||||||
|  |         if 'total_price' in specs | ||||||
|  |         else specs.get('price') | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
|     # Create a Hosting Order with vm_id = 0, we shall set it later in |     # Create a Hosting Order with vm_id = 0, we shall set it later in | ||||||
|     # celery task once the VM instance is up and running |     # celery task once the VM instance is up and running | ||||||
|     order = HostingOrder.create( |     order = HostingOrder.create( | ||||||
|         price=specs['price'], |         price=final_price, | ||||||
|         vm_id=0, |  | ||||||
|         customer=customer, |         customer=customer, | ||||||
|         billing_address=billing_address |         billing_address=billing_address, | ||||||
|  |         vm_pricing=vm_pricing | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     # Create a Hosting Bill |     # Create a Hosting Bill | ||||||
|  |  | ||||||
|  | @ -17,11 +17,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, create_vm | from .utils import get_cms_integration, create_vm | ||||||
| 
 | 
 | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
|  | @ -91,7 +91,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')) | ||||||
|  | @ -104,12 +105,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: | ||||||
|  | @ -137,14 +156,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 | ||||||
|  | @ -218,7 +244,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 | ||||||
| 
 | 
 | ||||||
|  | @ -391,7 +420,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) | ||||||
|  |  | ||||||
|  | @ -70,7 +70,7 @@ hr.small { | ||||||
|   } |   } | ||||||
|   .navbar-custom .navbar-brand { |   .navbar-custom .navbar-brand { | ||||||
|     color: white; |     color: white; | ||||||
|     padding: 20px; |     padding: 5px 20px; | ||||||
|   } |   } | ||||||
|   .navbar-custom .navbar-brand:hover, |   .navbar-custom .navbar-brand:hover, | ||||||
|   .navbar-custom .navbar-brand:focus { |   .navbar-custom .navbar-brand:focus { | ||||||
|  |  | ||||||
|  | @ -516,7 +516,7 @@ META_INCLUDE_KEYWORDS = ["ungleich", "hosting", "switzerland", | ||||||
|                          "Schweiz", "Swiss", "cdist"] |                          "Schweiz", "Swiss", "cdist"] | ||||||
| META_USE_SITES = True | META_USE_SITES = True | ||||||
| 
 | 
 | ||||||
| PARLER_LANGUAGES = {1: ({'code': 'en-us'}, {'code': 'de'},)} | PARLER_LANGUAGES = {SITE_ID: ({'code': 'en-us'}, {'code': 'de'},)} | ||||||
| AUTH_USER_MODEL = 'membership.CustomUser' | AUTH_USER_MODEL = 'membership.CustomUser' | ||||||
| 
 | 
 | ||||||
| # PAYMENT | # PAYMENT | ||||||
|  |  | ||||||
|  | @ -18,8 +18,8 @@ import debug_toolbar | ||||||
| 
 | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
|     url(r'^index.html$', LandingView.as_view()), |     url(r'^index.html$', LandingView.as_view()), | ||||||
|     url(r'^open_api/', include('opennebula_api.urls', |     url(r'^open_api/', | ||||||
|                                namespace='opennebula_api')), |         include('opennebula_api.urls', namespace='opennebula_api')), | ||||||
|     url(r'^railshosting/', RailsHostingView.as_view(), |     url(r'^railshosting/', RailsHostingView.as_view(), | ||||||
|         name="rails.hosting"), |         name="rails.hosting"), | ||||||
|     url(r'^nodehosting/', NodeJSHostingView.as_view(), |     url(r'^nodehosting/', NodeJSHostingView.as_view(), | ||||||
|  | @ -28,8 +28,7 @@ urlpatterns = [ | ||||||
|         name="django.hosting"), |         name="django.hosting"), | ||||||
|     url(r'^nosystemd/', include('nosystemd.urls', namespace="nosystemd")), |     url(r'^nosystemd/', include('nosystemd.urls', namespace="nosystemd")), | ||||||
|     url(r'^taggit_autosuggest/', include('taggit_autosuggest.urls')), |     url(r'^taggit_autosuggest/', include('taggit_autosuggest.urls')), | ||||||
|     url(r'^jsi18n/(?P<packages>\S+?)/$', |     url(r'^jsi18n/(?P<packages>\S+?)/$', i18n.javascript_catalog), | ||||||
|         i18n.javascript_catalog), |  | ||||||
| ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) | ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) | ||||||
| 
 | 
 | ||||||
| urlpatterns += i18n_patterns( | urlpatterns += i18n_patterns( | ||||||
|  | @ -45,29 +44,22 @@ urlpatterns += i18n_patterns( | ||||||
|     url(r'^admin/', include(admin.site.urls)), |     url(r'^admin/', include(admin.site.urls)), | ||||||
|     url(r'^datacenterlight/', |     url(r'^datacenterlight/', | ||||||
|         include('datacenterlight.urls', namespace="datacenterlight")), |         include('datacenterlight.urls', namespace="datacenterlight")), | ||||||
|     url(r'^hosting/', RedirectView.as_view( |     url(r'^hosting/', RedirectView.as_view(url=reverse_lazy('hosting:login')), | ||||||
|         url=reverse_lazy('hosting:login')), name='redirect_hosting_login'), |         name='redirect_hosting_login'), | ||||||
|     url(r'^alplora/', include('alplora.urls', namespace="alplora")), |     url(r'^alplora/', include('alplora.urls', namespace="alplora")), | ||||||
|     url(r'^membership/', include(membership_urls)), |     url(r'^membership/', include(membership_urls)), | ||||||
|     url(r'^digitalglarus/', include('digitalglarus.urls', |     url(r'^digitalglarus/', | ||||||
|                                     namespace="digitalglarus")), |         include('digitalglarus.urls', namespace="digitalglarus")), | ||||||
|     url(r'^cms/blog/', |     url(r'^cms/blog/', include('ungleich.urls', namespace='ungleich')), | ||||||
|         include('ungleich.urls', namespace='ungleich')), |     url(r'^blog/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<slug>\w[-\w]*)/$', | ||||||
|     url( |  | ||||||
|         r'^blog/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<slug>\w[-\w]*)/$', |  | ||||||
|         RedirectView.as_view(pattern_name='ungleich:post-detail')), |         RedirectView.as_view(pattern_name='ungleich:post-detail')), | ||||||
|     url(r'^blog/$', RedirectView.as_view( |     url(r'^blog/$', | ||||||
|                 url=reverse_lazy('ungleich:post-list') |         RedirectView.as_view(url=reverse_lazy('ungleich:post-list')), name='blog_list_view'), | ||||||
|             ), name='blog_list_view' |  | ||||||
|         ), |  | ||||||
|     url(r'^cms/', include('cms.urls')), |     url(r'^cms/', include('cms.urls')), | ||||||
|  |     url(r'^blog/', include('djangocms_blog.urls', namespace='djangocms_blog')), | ||||||
|     url(r'^$', RedirectView.as_view(url='/cms') if REDIRECT_TO_CMS |     url(r'^$', RedirectView.as_view(url='/cms') if REDIRECT_TO_CMS | ||||||
|         else LandingView.as_view()), |         else LandingView.as_view()), | ||||||
|     url(r'^', |     url(r'^', include('ungleich_page.urls', namespace='ungleich_page')), | ||||||
|         include('ungleich_page.urls', |  | ||||||
|                 namespace='ungleich_page'), |  | ||||||
|         name='ungleich_page'), |  | ||||||
|     url(r'^blog/', include('djangocms_blog.urls', namespace='djangocms_blog')), |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| urlpatterns += [ | urlpatterns += [ | ||||||
|  |  | ||||||
							
								
								
									
										23
									
								
								hosting/migrations/0044_hostingorder_vm_pricing.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								hosting/migrations/0044_hostingorder_vm_pricing.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Generated by Django 1.9.4 on 2018-04-16 00:22 | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
|  | from django.db import migrations, models | ||||||
|  | import django.db.models.deletion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('datacenterlight', '0019_auto_20180415_2236'), | ||||||
|  |         ('hosting', '0043_vmdetail'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AddField( | ||||||
|  |             model_name='hostingorder', | ||||||
|  |             name='vm_pricing', | ||||||
|  |             field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='datacenterlight.VMPricing'), | ||||||
|  |             preserve_default=False, | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
|  | @ -6,6 +6,8 @@ from django.db import models | ||||||
| from django.utils import timezone | from django.utils import timezone | ||||||
| from django.utils.functional import cached_property | from django.utils.functional import cached_property | ||||||
| from Crypto.PublicKey import RSA | from Crypto.PublicKey import RSA | ||||||
|  | 
 | ||||||
|  | from datacenterlight.models import VMPricing | ||||||
| from membership.models import StripeCustomer, CustomUser | from membership.models import StripeCustomer, CustomUser | ||||||
| from utils.models import BillingAddress | from utils.models import BillingAddress | ||||||
| from utils.mixins import AssignPermissionsMixin | from utils.mixins import AssignPermissionsMixin | ||||||
|  | @ -53,6 +55,7 @@ class HostingOrder(AssignPermissionsMixin, models.Model): | ||||||
|     stripe_charge_id = models.CharField(max_length=100, null=True) |     stripe_charge_id = models.CharField(max_length=100, null=True) | ||||||
|     price = models.FloatField() |     price = models.FloatField() | ||||||
|     subscription_id = models.CharField(max_length=100, null=True) |     subscription_id = models.CharField(max_length=100, null=True) | ||||||
|  |     vm_pricing = models.ForeignKey(VMPricing) | ||||||
| 
 | 
 | ||||||
|     permissions = ('view_hostingorder',) |     permissions = ('view_hostingorder',) | ||||||
| 
 | 
 | ||||||
|  | @ -70,12 +73,13 @@ class HostingOrder(AssignPermissionsMixin, models.Model): | ||||||
| 
 | 
 | ||||||
|     @classmethod |     @classmethod | ||||||
|     def create(cls, price=None, vm_id=None, customer=None, |     def create(cls, price=None, vm_id=None, customer=None, | ||||||
|                billing_address=None): |                billing_address=None, vm_pricing=None): | ||||||
|         instance = cls.objects.create( |         instance = cls.objects.create( | ||||||
|             price=price, |             price=price, | ||||||
|             vm_id=vm_id, |             vm_id=vm_id, | ||||||
|             customer=customer, |             customer=customer, | ||||||
|             billing_address=billing_address |             billing_address=billing_address, | ||||||
|  |             vm_pricing=vm_pricing | ||||||
|         ) |         ) | ||||||
|         instance.assign_permissions(customer.user) |         instance.assign_permissions(customer.user) | ||||||
|         return instance |         return instance | ||||||
|  |  | ||||||
|  | @ -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> | ||||||
|  |  | ||||||
|  | @ -43,7 +43,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 | ||||||
|  | @ -750,11 +750,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: | ||||||
|  | @ -763,6 +769,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, | ||||||
|  | @ -1094,7 +1111,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))) | ||||||
|  |  | ||||||
|  | @ -5,29 +5,34 @@ | ||||||
|     <!-- Brand and toggle get grouped for better mobile display --> |     <!-- Brand and toggle get grouped for better mobile display --> | ||||||
|     <div class="navbar-header page-scroll"> |     <div class="navbar-header page-scroll"> | ||||||
|       <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"> |       <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"> | ||||||
| 	<span class="sr-only">Toggle navigation</span> |     	 <span class="sr-only">Toggle navigation</span> | ||||||
| 	<span class="icon-bar"></span> |       	<span class="icon-bar"></span> | ||||||
| 	<span class="icon-bar"></span> |       	<span class="icon-bar"></span> | ||||||
| 	<span class="icon-bar"></span> |       	<span class="icon-bar"></span> | ||||||
|       </button> |       </button> | ||||||
|       <a class="navbar-brand" href="https://www.ungleich.ch"> |       <a class="navbar-brand" href="https://www.ungleich.ch"> | ||||||
| 	<img src="{% static "blog.ungleich.ch/img/logo_white.svg" %}" /> | 	      <img src="{% static "blog.ungleich.ch/img/logo_white.svg" %}" /> | ||||||
|       </a> |       </a> | ||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
|     <!-- Collect the nav links, forms, and other content for toggling --> |     <!-- Collect the nav links, forms, and other content for toggling --> | ||||||
|     <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> |     <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> | ||||||
|       <ul class="nav navbar-nav navbar-right"> |       <ul class="nav navbar-nav navbar-right"> | ||||||
| 	{% for child in children %} |         <li> | ||||||
| 	<li class="child{% if child.selected %} selected{% endif %}{% if child.ancestor %} ancestor{% endif %}{% if child.sibling %} sibling{% endif %}{% if child.descendant %} descendant{% endif %}"> |           <a href="{% url 'djangocms_blog:posts-latest' %}">Ungleich Blog</a> | ||||||
| 	  <a href="{{ child.attr.redirect_url|default:child.get_absolute_url }}">{{ child.get_menu_title }}</a> |         </li> | ||||||
| 	  {% if child.children %} |         {% comment %} | ||||||
| 	  <ul> |         	{% for child in children %} | ||||||
| 	    {% show_menu from_level to_level extra_inactive extra_active template "" "" child %} |           	<li class="child{% if child.selected %} selected{% endif %}{% if child.ancestor %} ancestor{% endif %}{% if child.sibling %} sibling{% endif %}{% if child.descendant %} descendant{% endif %}"> | ||||||
| 	  </ul> |           	  <a href="{{ child.attr.redirect_url|default:child.get_absolute_url }}">{{ child.get_menu_title }}</a> | ||||||
| 	  {% endif %} |           	  {% if child.children %} | ||||||
| 	</li> |           	  <ul> | ||||||
| 	{% endfor %} |           	    {% show_menu from_level to_level extra_inactive extra_active template "" "" child %} | ||||||
|  |           	  </ul> | ||||||
|  |           	  {% endif %} | ||||||
|  |           	</li> | ||||||
|  |         	{% endfor %} | ||||||
|  |         {% endcomment %} | ||||||
|       </ul> |       </ul> | ||||||
|     </div> |     </div> | ||||||
|     <!-- /.navbar-collapse --> |     <!-- /.navbar-collapse --> | ||||||
|  |  | ||||||
|  | @ -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