merged master
This commit is contained in:
		
				commit
				
					
						a5bd8347e8
					
				
			
		
					 32 changed files with 654 additions and 165 deletions
				
			
		
							
								
								
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -41,4 +41,5 @@ secret-key | |||
| /utils/optimize/ | ||||
| 
 | ||||
| # 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 | ||||
|     * #4367: [dcl] email logo resolution fix | ||||
|     * #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 | ||||
|     * #4396: [ungleich] add favicon to ungleich blog | ||||
|     * #4327: [dcl] fix navbar logo repeat | ||||
|  |  | |||
|  | @ -1,10 +1,18 @@ | |||
| from django.contrib import admin | ||||
| 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): | ||||
|     list_display = ('name', 'domain') | ||||
| 
 | ||||
| 
 | ||||
| class CMSFaviconExtensionAdmin(PageExtensionAdmin): | ||||
|     pass | ||||
| 
 | ||||
| 
 | ||||
| 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.pluginmodel import CMSPlugin | ||||
| from django.contrib.sites.models import Site | ||||
| from django.db import models | ||||
| from django.utils.safestring import mark_safe | ||||
| from djangocms_text_ckeditor.fields import HTMLField | ||||
| from filer.fields.file import FilerFileField | ||||
| from filer.fields.image import FilerImageField | ||||
| 
 | ||||
| from datacenterlight.models import VMPricing | ||||
| 
 | ||||
| 
 | ||||
| class CMSIntegration(models.Model): | ||||
|     name = models.CharField( | ||||
|  | @ -30,9 +35,15 @@ class CMSIntegration(models.Model): | |||
|         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): | ||||
|     heading = models.CharField( | ||||
|         blank=True, null=True, max_length=100, | ||||
|  | @ -275,3 +286,12 @@ class DCLSectionPromoPluginModel(CMSPlugin): | |||
|         if self.background_image: | ||||
|             extra_classes += ' promo-with-bg' | ||||
|         return extra_classes | ||||
| 
 | ||||
| 
 | ||||
| class DCLCustomPricingModel(CMSPlugin): | ||||
|     pricing = models.ForeignKey( | ||||
|         VMPricing, | ||||
|         related_name="dcl_custom_pricing_vm_pricing", | ||||
|         help_text='Choose a pricing that will be associated with this ' | ||||
|                   'Calculator' | ||||
|     ) | ||||
|  |  | |||
|  | @ -6,9 +6,9 @@ from .cms_models import ( | |||
|     DCLFooterPluginModel, DCLLinkPluginModel, DCLNavbarDropdownPluginModel, | ||||
|     DCLSectionIconPluginModel, DCLSectionImagePluginModel, | ||||
|     DCLSectionPluginModel, DCLNavbarPluginModel, | ||||
|     DCLSectionPromoPluginModel | ||||
|     DCLSectionPromoPluginModel, DCLCustomPricingModel | ||||
| ) | ||||
| from .models import VMTemplate | ||||
| from .models import VMTemplate, VMPricing | ||||
| 
 | ||||
| 
 | ||||
| @plugin_pool.register_plugin | ||||
|  | @ -75,13 +75,13 @@ class DCLSectionPromoPlugin(CMSPluginBase): | |||
| @plugin_pool.register_plugin | ||||
| class DCLCalculatorPlugin(CMSPluginBase): | ||||
|     module = "Datacenterlight" | ||||
|     name = "DCL Calculator Plugin" | ||||
|     name = "DCL Calculator Section Plugin" | ||||
|     model = DCLSectionPluginModel | ||||
|     render_template = "datacenterlight/cms/calculator.html" | ||||
|     cache = False | ||||
|     allow_children = True | ||||
|     child_classes = [ | ||||
|         'DCLSectionPromoPlugin', 'UngleichHTMLPlugin' | ||||
|         'DCLSectionPromoPlugin', 'UngleichHTMLPlugin', 'DCLCustomPricingPlugin' | ||||
|     ] | ||||
| 
 | ||||
|     def render(self, context, instance, placeholder): | ||||
|  | @ -89,15 +89,38 @@ class DCLCalculatorPlugin(CMSPluginBase): | |||
|             context, instance, placeholder | ||||
|         ) | ||||
|         context['templates'] = VMTemplate.objects.all() | ||||
|         context['children_to_side'] = [] | ||||
|         context['children_to_content'] = [] | ||||
|         pricing_plugin_model = None | ||||
|         if instance.child_plugin_instances is not None: | ||||
|             context['children_to_content'].extend( | ||||
|                 instance.child_plugin_instances | ||||
|             ) | ||||
|             for child in instance.child_plugin_instances: | ||||
|                 if child.__class__.__name__ == 'DCLCustomPricingModel': | ||||
|                     # The second clause is just to make sure we pick up the | ||||
|                     # most recent CustomPricing, if more than one is present | ||||
|                     if (pricing_plugin_model is None or child.pricing_id > | ||||
|                             pricing_plugin_model.model.pricing_id): | ||||
|                         pricing_plugin_model = child | ||||
| 
 | ||||
|         if pricing_plugin_model: | ||||
|             context['vm_pricing'] = VMPricing.get_vm_pricing_by_name( | ||||
|                 name=pricing_plugin_model.pricing.name | ||||
|             ) | ||||
|         else: | ||||
|             context['vm_pricing'] = VMPricing.get_default_pricing() | ||||
| 
 | ||||
|         return context | ||||
| 
 | ||||
| 
 | ||||
| @plugin_pool.register_plugin | ||||
| class DCLCustomPricingPlugin(CMSPluginBase): | ||||
|     module = "Datacenterlight" | ||||
|     name = "DCL Custom Pricing Plugin" | ||||
|     model = DCLCustomPricingModel | ||||
|     render_plugin = False | ||||
| 
 | ||||
| 
 | ||||
| @plugin_pool.register_plugin | ||||
| class DCLBannerListPlugin(CMSPluginBase): | ||||
|     module = "Datacenterlight" | ||||
|  |  | |||
							
								
								
									
										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 "" | ||||
| "Project-Id-Version: PACKAGE VERSION\n" | ||||
| "Report-Msgid-Bugs-To: \n" | ||||
| "POT-Creation-Date: 2018-03-30 21:29+0000\n" | ||||
| "POT-Creation-Date: 2018-04-17 19:26+0000\n" | ||||
| "PO-Revision-Date: 2018-03-30 23:22+0000\n" | ||||
| "Last-Translator: b'Anonymous User <coder.purple+25@gmail.com>'\n" | ||||
| "Language-Team: LANGUAGE <LL@li.org>\n" | ||||
|  | @ -72,9 +72,9 @@ msgstr "Data Center Light Account Aktivierung" | |||
| 
 | ||||
| #, python-format | ||||
| msgid "" | ||||
| "You can activate your Data Center Light account by clicking <a " | ||||
| "href=\"%(base_url)s%(activation_link)s\" style=\"text-decoration: none; " | ||||
| "color: #4382c8; font-weight: 400;\">here</a>." | ||||
| "You can activate your Data Center Light account by clicking <a href=" | ||||
| "\"%(base_url)s%(activation_link)s\" style=\"text-decoration: none; color: " | ||||
| "#4382c8; font-weight: 400;\">here</a>." | ||||
| msgstr "" | ||||
| "Klicke <a href=\"%(base_url)s%(activation_link)s\"style=\"text-decoration: " | ||||
| "none; color: #4382c8; font-weight: 400;\">hier</a> um deinen Data Center " | ||||
|  | @ -97,12 +97,13 @@ msgstr "Deine E-Mail-Adresse" | |||
| msgid "Password" | ||||
| msgstr "Passwort" | ||||
| 
 | ||||
| #, python-format | ||||
| msgid "" | ||||
| "You can reset your password <a href=\"%(base_url)s%(reset_password_url)s\" " | ||||
| "style=\"text-decoration: none; color: #4382c8; font-weight: 400;\">here</a>." | ||||
| msgstr "" | ||||
| "Du kannst dein Passwort <a href=\"%(base_url)s%(reset_password_url)s\" " | ||||
| "style=\"text-decoration: none; color: #4382c8; font-weight: 400;\">hier</a> " | ||||
| "Du kannst dein Passwort <a href=\"%(base_url)s%(reset_password_url)s\" style=" | ||||
| "\"text-decoration: none; color: #4382c8; font-weight: 400;\">hier</a> " | ||||
| "zurücksetzen." | ||||
| 
 | ||||
| msgid "Your Data Center Light Team" | ||||
|  | @ -160,21 +161,6 @@ msgstr "Weiter" | |||
| msgid "Home" | ||||
| msgstr "Home" | ||||
| 
 | ||||
| msgid "Highlights" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Scale out" | ||||
| msgstr "Skalierung" | ||||
| 
 | ||||
| msgid "Reliable and light" | ||||
| msgstr "Zuverlässig und leicht" | ||||
| 
 | ||||
| msgid "Pricing" | ||||
| msgstr "Preise" | ||||
| 
 | ||||
| msgid "Order VM" | ||||
| msgstr "VM bestellen" | ||||
| 
 | ||||
| msgid "Contact" | ||||
| msgstr "Kontakt" | ||||
| 
 | ||||
|  | @ -184,6 +170,9 @@ msgstr "Nutzungsbedingungen" | |||
| msgid "Finally, an affordable VM hosting in Switzerland!" | ||||
| msgstr "Endlich: bezahlbares VM Hosting in der Schweiz" | ||||
| 
 | ||||
| msgid "Highlights" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "I want it!" | ||||
| msgstr "Das will ich haben!" | ||||
| 
 | ||||
|  | @ -203,8 +192,8 @@ msgid "" | |||
| "order to make it more sustainable and affordable at the same time." | ||||
| msgstr "" | ||||
| "Ist kreativ, indem es sich ein modernes und alternatives Layout zu Nutze " | ||||
| "macht um Nachhaltigkeit zu fördern und somit erschwingliche Preise bieten zu" | ||||
| " können.
" | ||||
| "macht um Nachhaltigkeit zu fördern und somit erschwingliche Preise bieten zu " | ||||
| "können.
" | ||||
| 
 | ||||
| msgid "" | ||||
| "Cuts down the costs for you by using FOSS (Free Open Source Software) " | ||||
|  | @ -214,6 +203,9 @@ msgstr "" | |||
| "mit FOSS (Free Open Source Software) arbeitet und wir daher auf " | ||||
| "Lizenzgebühren verzichten können.
" | ||||
| 
 | ||||
| msgid "Scale out" | ||||
| msgstr "Skalierung" | ||||
| 
 | ||||
| msgid "" | ||||
| "We don't use special hardware. We use commodity hardware: we buy computers " | ||||
| "that you buy. Just many more and put them in a cozy home for computers " | ||||
|  | @ -223,6 +215,9 @@ msgstr "" | |||
| "erschwingliche Systeme. Bei grösserer Auslastung werden mehr " | ||||
| "Standardkomponenten hinzugekauft und skalieren so das Datencenter." | ||||
| 
 | ||||
| msgid "Reliable and light" | ||||
| msgstr "Zuverlässig und leicht" | ||||
| 
 | ||||
| msgid "" | ||||
| "Our VMs are located in Switzerland, with reliable power supply and fast " | ||||
| "internet connection. Our VM costs less thanks to our featherlight " | ||||
|  | @ -232,8 +227,7 @@ msgstr "" | |||
| "Energieversorgung, sowie schneller Internetverbindung ausgestattet. Unser " | ||||
| "Angebot ist aufgrund unserer leichten Infrastruktur überaus kostengünstig." | ||||
| 
 | ||||
| msgid "" | ||||
| "Simple and affordable: Try our virtual machine with featherlight price." | ||||
| msgid "Simple and affordable: Try our virtual machine with featherlight price." | ||||
| msgstr "" | ||||
| "Einfach und bezahlbar: Teste nun unsere virtuellen Maschinen mit " | ||||
| "federleichten Preisen." | ||||
|  | @ -314,6 +308,9 @@ msgstr "Gesamt" | |||
| msgid "including VAT" | ||||
| msgstr "inkl. Mehrwertsteuer" | ||||
| 
 | ||||
| msgid "excluding VAT" | ||||
| msgstr "exkl. Mehrwertsteuer" | ||||
| 
 | ||||
| msgid "Month" | ||||
| msgstr "Monat" | ||||
| 
 | ||||
|  | @ -321,20 +318,20 @@ msgid "Credit Card" | |||
| msgstr "Kreditkarte" | ||||
| 
 | ||||
| msgid "" | ||||
| "Please fill in your credit card information below. We are using <a " | ||||
| "href=\"https://stripe.com\" target=\"_blank\">Stripe</a> for payment and do " | ||||
| "not store your information in our database." | ||||
| "Please fill in your credit card information below. We are using <a href=" | ||||
| "\"https://stripe.com\" target=\"_blank\">Stripe</a> for payment and do not " | ||||
| "store your information in our database." | ||||
| msgstr "" | ||||
| "Bitte fülle Deine Kreditkarteninformationen unten aus. Wir nutzen <a " | ||||
| "href=\"https://stripe.com\" target=\"_blank\">Stripe</a> für die Bezahlung " | ||||
| "und speichern keine Informationen in unserer Datenbank." | ||||
| "Bitte fülle Deine Kreditkarteninformationen unten aus. Wir nutzen <a href=" | ||||
| "\"https://stripe.com\" target=\"_blank\">Stripe</a> für die Bezahlung und " | ||||
| "speichern keine Informationen in unserer Datenbank." | ||||
| 
 | ||||
| msgid "" | ||||
| "You are not making any payment yet. After submitting your card information, " | ||||
| "you will be taken to the Confirm Order Page." | ||||
| msgstr "" | ||||
| "Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst ausgelöst," | ||||
| " nachdem Du die Bestellung auf der nächsten Seite bestätigt hast." | ||||
| "Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst ausgelöst, " | ||||
| "nachdem Du die Bestellung auf der nächsten Seite bestätigt hast." | ||||
| 
 | ||||
| msgid "Card Number" | ||||
| msgstr "Kreditkartennummer" | ||||
|  | @ -352,8 +349,8 @@ msgid "" | |||
| "You are not making any payment yet. After placing your order, you will be " | ||||
| "taken to the Submit Payment Page." | ||||
| msgstr "" | ||||
| "Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst ausgelöst," | ||||
| " nachdem Du die Bestellung auf der nächsten Seite bestätigt hast." | ||||
| "Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst ausgelöst, " | ||||
| "nachdem Du die Bestellung auf der nächsten Seite bestätigt hast." | ||||
| 
 | ||||
| msgid "Processing" | ||||
| msgstr "Weiter" | ||||
|  | @ -383,13 +380,18 @@ msgstr "Bestellungsübersicht" | |||
| msgid "Product" | ||||
| msgstr "Produkt" | ||||
| 
 | ||||
| #, python-format | ||||
| msgid "Subtotal" | ||||
| msgstr "Zwischensumme" | ||||
| 
 | ||||
| msgid "VAT" | ||||
| msgstr "Mehrwertsteuer" | ||||
| 
 | ||||
| msgid "" | ||||
| "By clicking \"Place order\" this plan will charge your credit card account " | ||||
| "with the fee of %(vm_price)sCHF/month" | ||||
| "with the fee of %(vm_total_price)s CHF/month" | ||||
| msgstr "" | ||||
| "Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(vm_price)sCHF " | ||||
| "pro Monat belastet" | ||||
| "Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit " | ||||
| "%(vm_total_price)s CHF pro Monat belastet" | ||||
| 
 | ||||
| msgid "Place order" | ||||
| msgstr "Bestellen" | ||||
|  | @ -455,25 +457,25 @@ msgstr "Wir unterstützen die FOSS Community." | |||
| msgid "" | ||||
| "Data Center Light is the child of free and open source software (FOSS) " | ||||
| "movement. <br>We grew up with it, live by it, and believe in it.<br> The " | ||||
| "more we work on our data center,<br> the more we contribute back to the FOSS" | ||||
| " community." | ||||
| "more we work on our data center,<br> the more we contribute back to the FOSS " | ||||
| "community." | ||||
| msgstr "" | ||||
| "Data Center Light ist ein Teil der Free und Opens Source Software (FOSS) " | ||||
| "Bewegung.<br/> Wir sind damit gross geworden, leben damit und glauben " | ||||
| "daran.<br/> Je weiter wir mit unserem Data Center Light vorankommen, desto " | ||||
| "mehr können wir etwas an die FOSS Community zurückgeben." | ||||
| "Bewegung.<br/> Wir sind damit gross geworden, leben damit und glauben daran." | ||||
| "<br/> Je weiter wir mit unserem Data Center Light vorankommen, desto mehr " | ||||
| "können wir etwas an die FOSS Community zurückgeben." | ||||
| 
 | ||||
| msgid "We bring the future to you." | ||||
| msgstr "Wir bringen die Zukunft zu dir." | ||||
| 
 | ||||
| msgid "" | ||||
| "Data Center Light uses the most modern technologies out there.<br>Your VM " | ||||
| "needs only IPv6. Data Center Light provides<br> transparent two-way " | ||||
| "IPv6/IPv4 translation." | ||||
| "needs only IPv6. Data Center Light provides<br> transparent two-way IPv6/" | ||||
| "IPv4 translation." | ||||
| msgstr "" | ||||
| "Data Center Light verwendet die zur Zeit modernsten Technologien.<br/>Deine " | ||||
| "VM läuft mit IPv6. Data Center Light bietet eine transparente " | ||||
| "IPv6/IPv4-Zweiweglösung." | ||||
| "VM läuft mit IPv6. Data Center Light bietet eine transparente IPv6/IPv4-" | ||||
| "Zweiweglösung." | ||||
| 
 | ||||
| msgid "" | ||||
| " No more spinning metal plates! Data Center Light uses only SSDs. We keep " | ||||
|  | @ -497,6 +499,10 @@ msgstr "Ungültige RAM-Grösse" | |||
| msgid "Invalid storage size" | ||||
| msgstr "Ungültige Speicher-Grösse" | ||||
| 
 | ||||
| #, python-brace-format | ||||
| msgid "Incorrect pricing name. Please contact support{support_email}" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Confirm Order" | ||||
| msgstr "Bestellung Bestätigen" | ||||
| 
 | ||||
|  | @ -507,8 +513,8 @@ msgid "" | |||
| "There was a payment related error. On close of this popup, you will be " | ||||
| "redirected back to the payment page." | ||||
| msgstr "" | ||||
| "Es ist ein Fehler bei der Zahlung betreten. Du wirst nach dem Schliessen vom" | ||||
| " Popup zur Bezahlseite weitergeleitet." | ||||
| "Es ist ein Fehler bei der Zahlung betreten. Du wirst nach dem Schliessen vom " | ||||
| "Popup zur Bezahlseite weitergeleitet." | ||||
| 
 | ||||
| msgid "Thank you for the order." | ||||
| msgstr "Danke für Deine Bestellung." | ||||
|  | @ -517,8 +523,14 @@ msgid "" | |||
| "Your VM will be up and running in a few moments. We will send you a " | ||||
| "confirmation email as soon as it is ready." | ||||
| msgstr "" | ||||
| "Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du" | ||||
| " auf sie zugreifen kannst." | ||||
| "Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du " | ||||
| "auf sie zugreifen kannst." | ||||
| 
 | ||||
| #~ msgid "Pricing" | ||||
| #~ msgstr "Preise" | ||||
| 
 | ||||
| #~ msgid "Order VM" | ||||
| #~ msgstr "VM bestellen" | ||||
| 
 | ||||
| #~ msgid "Enter name" | ||||
| #~ msgstr "Name" | ||||
|  | @ -533,18 +545,19 @@ msgstr "" | |||
| #~ msgstr "Anfrage verschickt" | ||||
| 
 | ||||
| #~ msgid "" | ||||
| #~ "Thank you for your subscription! You will receive a confirmation mail from " | ||||
| #~ "our team" | ||||
| #~ "Thank you for your subscription! You will receive a confirmation mail " | ||||
| #~ "from our team" | ||||
| #~ msgstr "" | ||||
| #~ "Vielen dank für Ihre Anmeldung. Sie erhalten in kürze eine Bestätigungsmail " | ||||
| #~ "von unserem Team" | ||||
| #~ "Vielen dank für Ihre Anmeldung. Sie erhalten in kürze eine " | ||||
| #~ "Bestätigungsmail von unserem Team" | ||||
| 
 | ||||
| #~ msgid "Thank you for your request." | ||||
| #~ msgstr "Vielen Dank für Deine Anfrage." | ||||
| 
 | ||||
| #~ msgid "You are one step away from being our beta tester!" | ||||
| #~ msgstr "" | ||||
| #~ "Sie sind nur noch einen Schritt davon entfernt, unser Beta-Tester zu werden!" | ||||
| #~ "Sie sind nur noch einen Schritt davon entfernt, unser Beta-Tester zu " | ||||
| #~ "werden!" | ||||
| 
 | ||||
| #~ msgid "" | ||||
| #~ "Currently we are running our tests to make sure everything runs perfectly." | ||||
|  | @ -553,8 +566,8 @@ msgstr "" | |||
| #~ "sicherzustellen." | ||||
| 
 | ||||
| #~ msgid "" | ||||
| #~ "In the meantime, we would like to ask you a little patience<br/> until our " | ||||
| #~ "team contacts you with beta access." | ||||
| #~ "In the meantime, we would like to ask you a little patience<br/> until " | ||||
| #~ "our team contacts you with beta access." | ||||
| #~ msgstr "" | ||||
| #~ "Wir werden dann sobald als möglich Ihren Beta-Zugang erstellen und Sie " | ||||
| #~ "daraufhin kontaktieren.Bis dahin bitten wir Sie um etwas Geduld." | ||||
|  | @ -564,8 +577,8 @@ msgstr "" | |||
| 
 | ||||
| #~ msgid "Thank you for order! Our team will contact you via email" | ||||
| #~ msgstr "" | ||||
| #~ "Vielen Dank für die Bestellung. Unser Team setzt sich sobald wie möglich mit" | ||||
| #~ " Dir via E-Mail in Verbindung." | ||||
| #~ "Vielen Dank für die Bestellung. Unser Team setzt sich sobald wie möglich " | ||||
| #~ "mit Dir via E-Mail in Verbindung." | ||||
| 
 | ||||
| #~ msgid "Affordable VM hosting based in Switzerland" | ||||
| #~ msgstr "Bezahlbares VM Hosting in der Schweiz" | ||||
|  | @ -581,18 +594,18 @@ msgstr "" | |||
| 
 | ||||
| #~ msgid "" | ||||
| #~ "Our VMs are hosted in Glarus, Switzerland, and our website is currently " | ||||
| #~ "running in BETA mode. If you want more information that you did not find on " | ||||
| #~ "our website, or if your order is more detailed, or if you encounter any " | ||||
| #~ "technical hiccups, please contact us at support@datacenterlight.ch, our team" | ||||
| #~ " will get in touch with you asap." | ||||
| #~ "running in BETA mode. If you want more information that you did not find " | ||||
| #~ "on our website, or if your order is more detailed, or if you encounter " | ||||
| #~ "any technical hiccups, please contact us at support@datacenterlight.ch, " | ||||
| #~ "our team will get in touch with you asap." | ||||
| #~ msgstr "" | ||||
| #~ "Unsere VMs werden in der Schweiz im Kanton Glarus gehostet und befinden sich" | ||||
| #~ " zur Zeit noch in der BETA-Phase. Möchtest du mehr über uns erfahren und " | ||||
| #~ "hast auf unserer Website nicht genügend Informationen gefunden? Möchtest " | ||||
| #~ "eine detailliertere Bestellung aufgeben? Bist du auf technische Probleme " | ||||
| #~ "gestossen, die du uns mitteilen möchtest? Dann zögere nicht und kontaktiere " | ||||
| #~ "uns unter support@datacenterlight.ch. Unser Team wird sich umgehend um dein " | ||||
| #~ "Anliegen kümmern!" | ||||
| #~ "Unsere VMs werden in der Schweiz im Kanton Glarus gehostet und befinden " | ||||
| #~ "sich zur Zeit noch in der BETA-Phase. Möchtest du mehr über uns erfahren " | ||||
| #~ "und hast auf unserer Website nicht genügend Informationen gefunden? " | ||||
| #~ "Möchtest eine detailliertere Bestellung aufgeben? Bist du auf technische " | ||||
| #~ "Probleme gestossen, die du uns mitteilen möchtest? Dann zögere nicht und " | ||||
| #~ "kontaktiere uns unter support@datacenterlight.ch. Unser Team wird sich " | ||||
| #~ "umgehend um dein Anliegen kümmern!" | ||||
| 
 | ||||
| #~ msgid "is not a proper name" | ||||
| #~ msgstr "ist kein gültiger Name" | ||||
|  | @ -610,12 +623,14 @@ msgstr "" | |||
| #~ "\n" | ||||
| #~ "Hi,\n" | ||||
| #~ "\n" | ||||
| #~ "You can activate your %(dcl_text)s account by clicking here %(base_url)s%(activation_link)s\n" | ||||
| #~ "You can activate your %(dcl_text)s account by clicking here %(base_url)s" | ||||
| #~ "%(activation_link)s\n" | ||||
| #~ msgstr "" | ||||
| #~ "\n" | ||||
| #~ "Hallo,\n" | ||||
| #~ "\n" | ||||
| #~ "Du kannst deinen %(dcl_text)s Account aktivieren, indem du hier klickst %(base_url)s%(activation_link)s\n" | ||||
| #~ "Du kannst deinen %(dcl_text)s Account aktivieren, indem du hier klickst " | ||||
| #~ "%(base_url)s%(activation_link)s\n" | ||||
| 
 | ||||
| #~ msgid "Your" | ||||
| #~ msgstr "Dein" | ||||
|  | @ -650,12 +665,14 @@ msgstr "" | |||
| #~ msgid "I want to have it!" | ||||
| #~ msgstr "Das möchte ich haben!" | ||||
| 
 | ||||
| #~ msgid "Reuse existing factory halls intead of building an expensive building." | ||||
| #~ msgid "" | ||||
| #~ "Reuse existing factory halls intead of building an expensive building." | ||||
| #~ msgstr "" | ||||
| #~ "Nachhaltigkeit: Wiederverwendung ehemaliger Fabrikhallen an Stelle der " | ||||
| #~ "Errichtung eines neuen Gebäudes" | ||||
| 
 | ||||
| #~ msgid "Being creative, using modern and alternative design for a datacenter." | ||||
| #~ msgid "" | ||||
| #~ "Being creative, using modern and alternative design for a datacenter." | ||||
| #~ msgstr "" | ||||
| #~ "Kreativität: Verwendung eines modernen und alternativen Designs für unser " | ||||
| #~ "Datencenter" | ||||
|  | @ -678,8 +695,8 @@ msgstr "" | |||
| #~ msgstr "Standort des Datacenters ist in der Schweiz" | ||||
| 
 | ||||
| #~ msgid "" | ||||
| #~ " WARNING: We are currently running in BETA mode. We hope you won't encounter" | ||||
| #~ " any hiccups, but if you do, please let us know at " | ||||
| #~ " WARNING: We are currently running in BETA mode. We hope you won't " | ||||
| #~ "encounter any hiccups, but if you do, please let us know at " | ||||
| #~ "support@datacenterlight.ch" | ||||
| #~ msgstr "" | ||||
| #~ " Achtung: Wir befinden uns zurzeit im Beta-Release. Wir hoffen, dass Sie " | ||||
|  | @ -693,8 +710,8 @@ msgstr "" | |||
| #~ msgstr "Unser Versprechen" | ||||
| 
 | ||||
| #~ msgid "" | ||||
| #~ "Instead of creating an expensive SLA for availability, we promise that we do" | ||||
| #~ " our best to run things as smooth as possible." | ||||
| #~ "Instead of creating an expensive SLA for availability, we promise that we " | ||||
| #~ "do our best to run things as smooth as possible." | ||||
| #~ msgstr "" | ||||
| #~ "Anstatt eines SLAs (Service Levle Agreements) zu vereinbaren,setzen wir " | ||||
| #~ "unsere persönliche Arbeitskraft ein, um Ihnen ein sorgenfreiesHosting zu " | ||||
|  |  | |||
|  | @ -0,0 +1,36 @@ | |||
| from django.core.management.base import BaseCommand | ||||
| 
 | ||||
| from datacenterlight.models import VMPricing | ||||
| 
 | ||||
| 
 | ||||
| class Command(BaseCommand): | ||||
|     help = '''Creates default VMPricing object''' | ||||
|     DEFAULT_VMPRICING_NAME = 'default' | ||||
| 
 | ||||
|     def handle(self, *args, **options): | ||||
|         self.create_default_vm_pricing() | ||||
| 
 | ||||
|     def create_default_vm_pricing(self): | ||||
|         obj, created = VMPricing.objects.get_or_create( | ||||
|             name=self.DEFAULT_VMPRICING_NAME, | ||||
|             defaults={ | ||||
|                 "vat_inclusive": True, | ||||
|                 "cores_unit_price": 5, | ||||
|                 "ram_unit_price": 2, | ||||
|                 "ssd_unit_price": 0.6, | ||||
|                 "hdd_unit_price": 0.01 | ||||
|             } | ||||
|         ) | ||||
| 
 | ||||
|         if created: | ||||
|             print( | ||||
|                 'Successfully created {} VMPricing object'.format( | ||||
|                     self.DEFAULT_VMPRICING_NAME | ||||
|                 ) | ||||
|             ) | ||||
|         else: | ||||
|             print( | ||||
|                 '{} VMPricing exists already.'.format( | ||||
|                     self.DEFAULT_VMPRICING_NAME | ||||
|                 ) | ||||
|             ) | ||||
							
								
								
									
										45
									
								
								datacenterlight/migrations/0019_auto_20180415_2236.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								datacenterlight/migrations/0019_auto_20180415_2236.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| # Generated by Django 1.9.4 on 2018-04-15 22:36 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| import django.db.models.deletion | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('cms', '0014_auto_20160404_1908'), | ||||
|         ('datacenterlight', '0018_auto_20180403_1930'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.CreateModel( | ||||
|             name='DCLCustomPricingModel', | ||||
|             fields=[ | ||||
|                 ('cmsplugin_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='cms.CMSPlugin')), | ||||
|             ], | ||||
|             options={ | ||||
|                 'abstract': False, | ||||
|             }, | ||||
|             bases=('cms.cmsplugin',), | ||||
|         ), | ||||
|         migrations.CreateModel( | ||||
|             name='VMPricing', | ||||
|             fields=[ | ||||
|                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||||
|                 ('name', models.CharField(max_length=255, unique=True)), | ||||
|                 ('vat_inclusive', models.BooleanField(default=True)), | ||||
|                 ('vat_percentage', models.DecimalField(blank=True, decimal_places=5, default=0, max_digits=7)), | ||||
|                 ('cores_unit_price', models.DecimalField(decimal_places=5, default=0, max_digits=7)), | ||||
|                 ('ram_unit_price', models.DecimalField(decimal_places=5, default=0, max_digits=7)), | ||||
|                 ('ssd_unit_price', models.DecimalField(decimal_places=5, default=0, max_digits=7)), | ||||
|                 ('hdd_unit_price', models.DecimalField(decimal_places=6, default=0, max_digits=7)), | ||||
|             ], | ||||
|         ), | ||||
|         migrations.AddField( | ||||
|             model_name='dclcustompricingmodel', | ||||
|             name='pricing', | ||||
|             field=models.ForeignKey(help_text='Choose a pricing that will be associated with this Calculator', on_delete=django.db.models.deletion.CASCADE, related_name='dcl_custom_pricing_vm_pricing', to='datacenterlight.VMPricing'), | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										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 | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| 
 | ||||
| class VMTemplate(models.Model): | ||||
|     name = models.CharField(max_length=50) | ||||
|  | @ -12,6 +16,59 @@ class VMTemplate(models.Model): | |||
|         return vm_template | ||||
| 
 | ||||
| 
 | ||||
| class VMPricing(models.Model): | ||||
|     name = models.CharField(max_length=255, unique=True) | ||||
|     vat_inclusive = models.BooleanField(default=True) | ||||
|     vat_percentage = models.DecimalField( | ||||
|         max_digits=7, decimal_places=5, blank=True, default=0 | ||||
|     ) | ||||
|     cores_unit_price = models.DecimalField( | ||||
|         max_digits=7, decimal_places=5, default=0 | ||||
|     ) | ||||
|     ram_unit_price = models.DecimalField( | ||||
|         max_digits=7, decimal_places=5, default=0 | ||||
|     ) | ||||
|     ssd_unit_price = models.DecimalField( | ||||
|         max_digits=7, decimal_places=5, default=0 | ||||
|     ) | ||||
|     hdd_unit_price = models.DecimalField( | ||||
|         max_digits=7, decimal_places=6, default=0 | ||||
|     ) | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         return self.name + ' => ' + ' - '.join([ | ||||
|             '{}/Core'.format(self.cores_unit_price.normalize()), | ||||
|             '{}/GB RAM'.format(self.ram_unit_price.normalize()), | ||||
|             '{}/GB SSD'.format(self.ssd_unit_price.normalize()), | ||||
|             '{}/GB HDD'.format(self.hdd_unit_price.normalize()), | ||||
|             '{}% VAT'.format(self.vat_percentage.normalize()) | ||||
|             if not self.vat_inclusive else 'VAT-Incl', ] | ||||
|         ) | ||||
| 
 | ||||
|     @classmethod | ||||
|     def get_vm_pricing_by_name(cls, name): | ||||
|         try: | ||||
|             pricing = VMPricing.objects.get(name=name) | ||||
|         except Exception as e: | ||||
|             logger.error( | ||||
|                 "Error getting VMPricing with name {name}. " | ||||
|                 "Details: {details}. Attempting to return default" | ||||
|                 "pricing.".format(name=name, details=str(e)) | ||||
|             ) | ||||
|             pricing = VMPricing.get_default_pricing() | ||||
|         return pricing | ||||
| 
 | ||||
|     @classmethod | ||||
|     def get_default_pricing(cls): | ||||
|         """ Returns the default pricing or None """ | ||||
|         try: | ||||
|             default_pricing = VMPricing.objects.get(name='default') | ||||
|         except Exception as e: | ||||
|             logger.error(str(e)) | ||||
|             default_pricing = None | ||||
|         return default_pricing | ||||
| 
 | ||||
| 
 | ||||
| class StripePlan(models.Model): | ||||
|     """ | ||||
|     A model to store Data Center Light's created Stripe plans | ||||
|  |  | |||
|  | @ -120,6 +120,11 @@ | |||
|     .header_slider .intro-cap { | ||||
|         font-size: 3.25em; | ||||
|     } | ||||
| 
 | ||||
|     .header_slider > .carousel .item .container { | ||||
|         padding-left: 0; | ||||
|         padding-right: 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| .header_slider .intro_lead { | ||||
|  |  | |||
|  | @ -171,7 +171,18 @@ | |||
|     } | ||||
| 
 | ||||
|     function _calcPricing() { | ||||
|         var total = (cardPricing['cpu'].value * 5) + (2 * cardPricing['ram'].value) + (0.6 * cardPricing['storage'].value); | ||||
|         if(typeof window.coresUnitPrice === 'undefined'){ | ||||
|             window.coresUnitPrice = 5; | ||||
|         } | ||||
|         if(typeof window.ramUnitPrice === 'undefined'){ | ||||
|             window.coresUnitPrice = 2; | ||||
|         } | ||||
|         if(typeof window.ssdUnitPrice === 'undefined'){ | ||||
|             window.ssdUnitPrice = 0.6; | ||||
|         } | ||||
|         var total = (cardPricing['cpu'].value * window.coresUnitPrice) + | ||||
|                     (cardPricing['ram'].value * window.ramUnitPrice) + | ||||
|                     (cardPricing['storage'].value * window.ssdUnitPrice); | ||||
|         total = parseFloat(total.toFixed(2)); | ||||
|         $("#total").text(total); | ||||
|     } | ||||
|  |  | |||
|  | @ -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.stripe_utils import StripeUtils | ||||
| 
 | ||||
| from .models import VMPricing | ||||
| 
 | ||||
| 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)) | ||||
|     vm_id = None | ||||
|     try: | ||||
|         final_price = ( | ||||
|             specs.get('total_price') if 'total_price' in specs | ||||
|             else specs.get('price') | ||||
|         ) | ||||
| 
 | ||||
|         if 'pass' in user: | ||||
|             on_user = user.get('email') | ||||
|             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: | ||||
|             emsg = "Could not update subscription metadata for {sub}".format( | ||||
|                     sub=hosting_order.subscription_id | ||||
|                 ) | ||||
|                 sub=hosting_order.subscription_id | ||||
|             ) | ||||
|             logger.error(emsg) | ||||
|             if error_msg: | ||||
|                 error_msg += emsg | ||||
|  | @ -126,7 +133,7 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): | |||
|             'cores': specs.get('cpu'), | ||||
|             'memory': specs.get('memory'), | ||||
|             'storage': specs.get('disk_size'), | ||||
|             'price': specs.get('price'), | ||||
|             'price': final_price, | ||||
|             'template': template.get('name'), | ||||
|             'vm_name': vm.get('name'), | ||||
|             'vm_id': vm['vm_id'], | ||||
|  | @ -135,6 +142,10 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): | |||
| 
 | ||||
|         if 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 = { | ||||
|             'subject': settings.DCL_TEXT + " Order from %s" % context['email'], | ||||
|             'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, | ||||
|  |  | |||
|  | @ -8,9 +8,9 @@ | |||
|     <meta charset="utf-8"> | ||||
|     <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||||
|     <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"> | ||||
|     <title>{% page_attribute page_title %}</title> | ||||
|     <title>{% page_attribute "page_title" %}</title> | ||||
| 
 | ||||
|     <!-- Vendor CSS --> | ||||
|     <!-- Bootstrap Core CSS --> | ||||
|  | @ -30,7 +30,11 @@ | |||
|     <!-- External Fonts --> | ||||
|     <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 --> | ||||
|     <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> | ||||
|  | @ -52,7 +56,7 @@ | |||
|     {% placeholder 'Datacenterlight Header' or %} | ||||
|         <div class="dcl-header"> | ||||
|             <div class="container"> | ||||
|                 <h1>{% page_attribute page_title %}</h1> | ||||
|                 <h1>{% page_attribute "page_title" %}</h1> | ||||
|             </div> | ||||
|         </div> | ||||
|     {% endplaceholder %} | ||||
|  |  | |||
|  | @ -1,4 +1,16 @@ | |||
| {% load staticfiles i18n%} | ||||
| 
 | ||||
| {% if vm_pricing %} | ||||
|     <script type="application/javascript"> | ||||
|         window.vat_inclusive = {% if vm_pricing.vat_inclusive %}true{% else %}false{% endif%}; | ||||
|         window.vat_percentage = {{vm_pricing.vat_percentage|default:0}}; | ||||
|         window.coresUnitPrice = {{vm_pricing.cores_unit_price|default:0}}; | ||||
|         window.ramUnitPrice = {{vm_pricing.ram_unit_price|default:0}}; | ||||
|         window.ssdUnitPrice = {{vm_pricing.ssd_unit_price|default:0}}; | ||||
|         window.hddUnitPrice = {{vm_pricing.hdd_unit_price|default:0}}; | ||||
|     </script> | ||||
| {% endif %} | ||||
| 
 | ||||
| <form id="order_form" method="POST" action="{% url 'datacenterlight:index' %}" data-toggle="validator" role="form"> | ||||
|     {% csrf_token %} | ||||
|     <div class="title"> | ||||
|  | @ -7,9 +19,11 @@ | |||
|     <div class="price"> | ||||
|         <span id="total">15</span> | ||||
|         <span>CHF/{% trans "month" %}</span> | ||||
|         {% if vm_pricing.vat_inclusive %} | ||||
|         <div class="price-text"> | ||||
|             <p>{% trans "VAT included" %}</p> | ||||
|         </div> | ||||
|         {% endif %} | ||||
|     </div> | ||||
|     <div class="descriptions"> | ||||
|         <div class="description form-group"> | ||||
|  | @ -78,5 +92,6 @@ | |||
|             </select> | ||||
|         </div> | ||||
|     </div> | ||||
|     <input type="hidden" name="pricing_name" value="{% if vm_pricing.name %}{{vm_pricing.name}}{% else %}unknown{% endif%}"></input> | ||||
|     <input type="submit" class="btn btn-primary disabled" value="{% trans 'Continue' %}"></input> | ||||
| </form> | ||||
|  |  | |||
|  | @ -78,7 +78,7 @@ | |||
|                         <hr> | ||||
|                         <p>{% trans "Configuration"%} <strong class="pull-right">{{request.session.template.name}}</strong></p> | ||||
|                         <hr> | ||||
|                         <p class="last-p"><strong>{%trans "Total" %}</strong>  <small>({%trans "including VAT" %})</small> <strong class="pull-right">{{request.session.specs.price|intcomma}} CHF/{% trans "Month" %}</strong></p> | ||||
|                         <p class="last-p"><strong>{%trans "Total" %}</strong>  <small>({% if vm_pricing.vat_inclusive %}{%trans "including VAT" %}{% else %}{%trans "excluding VAT" %}{% endif %})</small> <strong class="pull-right">{{request.session.specs.price|intcomma}} CHF/{% trans "Month" %}</strong></p> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|  |  | |||
|  | @ -65,9 +65,19 @@ | |||
|                             <span>{% trans "Disk space" %}: </span> | ||||
|                             <span class="pull-right">{{vm.disk_size|intcomma}} GB</span> | ||||
|                         </p> | ||||
|                         {% if vm.vat > 0 %} | ||||
|                             <p> | ||||
|                                 <strong>{% trans "Subtotal" %}: </strong> | ||||
|                                 <span class="pull-right">{{vm.price|floatformat:2|intcomma}} CHF</span> | ||||
|                             </p> | ||||
|                             <p> | ||||
|                                 <span>{% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%): </span> | ||||
|                                 <span class="pull-right">{{vm.vat|floatformat:2|intcomma}} CHF</span> | ||||
|                             </p> | ||||
|                         {% endif %} | ||||
|                         <p> | ||||
|                             <span>{% trans "Total" %}</span> | ||||
|                             <span class="pull-right">{{vm.price|intcomma}} CHF</span> | ||||
|                             <strong>{% trans "Total" %}</strong> | ||||
|                             <span class="pull-right">{{vm.total_price|floatformat:2|intcomma}} CHF</span> | ||||
|                         </p> | ||||
|                     </div> | ||||
|                 </div> | ||||
|  | @ -78,7 +88,7 @@ | |||
|             {% csrf_token %} | ||||
|             <div class="row"> | ||||
|                 <div class="col-sm-8"> | ||||
|                     <div class="dcl-place-order-text">{% blocktrans with vm_price=request.session.specs.price %}By clicking "Place order" this plan will charge your credit card account with the fee of {{ vm_price }}CHF/month{% endblocktrans %}.</div> | ||||
|                     <div class="dcl-place-order-text">{% blocktrans with vm_total_price=vm.total_price|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with the fee of {{vm_total_price}} CHF/month{% endblocktrans %}.</div> | ||||
|                 </div> | ||||
|                 <div class="col-sm-4 order-confirm-btn text-right"> | ||||
|                     <button class="btn choice-btn" id="btn-create-vm" data-toggle="modal" data-target="#createvm-modal"> | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ from hosting.models import HostingOrder, HostingBill | |||
| from membership.models import StripeCustomer | ||||
| from utils.forms import UserBillingAddressForm | ||||
| from utils.models import BillingAddress | ||||
| from .models import VMPricing | ||||
| from .cms_models import CMSIntegration | ||||
| 
 | ||||
| 
 | ||||
|  | @ -30,16 +31,26 @@ def create_vm(billing_address_data, stripe_customer_id, specs, | |||
|         country=billing_address_data['country'] | ||||
|     ) | ||||
|     billing_address.save() | ||||
| 
 | ||||
|     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 | ||||
|     # celery task once the VM instance is up and running | ||||
|     order = HostingOrder.create( | ||||
|         price=specs['price'], | ||||
|         vm_id=0, | ||||
|         price=final_price, | ||||
|         customer=customer, | ||||
|         billing_address=billing_address | ||||
|         billing_address=billing_address, | ||||
|         vm_pricing=vm_pricing | ||||
|     ) | ||||
| 
 | ||||
|     # Create a Hosting Bill | ||||
|  |  | |||
|  | @ -17,11 +17,11 @@ from hosting.models import HostingOrder | |||
| from membership.models import CustomUser, StripeCustomer | ||||
| from opennebula_api.serializers import VMTemplateSerializer | ||||
| from utils.forms import BillingAddressForm, BillingAddressFormSignup | ||||
| from utils.hosting_utils import get_vm_price | ||||
| from utils.hosting_utils import get_vm_price_with_vat | ||||
| from utils.stripe_utils import StripeUtils | ||||
| from utils.tasks import send_plain_email_task | ||||
| from .forms import ContactForm | ||||
| from .models import VMTemplate | ||||
| from .models import VMTemplate, VMPricing | ||||
| from .utils import get_cms_integration, create_vm | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
|  | @ -91,7 +91,8 @@ class IndexView(CreateView): | |||
| 
 | ||||
|     @cache_control(no_cache=True, must_revalidate=True, no_store=True) | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         for session_var in ['specs', 'user', 'billing_address_data']: | ||||
|         for session_var in ['specs', 'user', 'billing_address_data', | ||||
|                             'pricing_name']: | ||||
|             if session_var in request.session: | ||||
|                 del request.session[session_var] | ||||
|         return HttpResponseRedirect(reverse('datacenterlight:cms_index')) | ||||
|  | @ -104,12 +105,30 @@ class IndexView(CreateView): | |||
|         storage = request.POST.get('storage') | ||||
|         storage_field = forms.IntegerField(validators=[self.validate_storage]) | ||||
|         template_id = int(request.POST.get('config')) | ||||
|         pricing_name = request.POST.get('pricing_name') | ||||
|         vm_pricing = VMPricing.get_vm_pricing_by_name(pricing_name) | ||||
| 
 | ||||
|         template = VMTemplate.objects.filter( | ||||
|             opennebula_vm_template_id=template_id | ||||
|         ).first() | ||||
|         template_data = VMTemplateSerializer(template).data | ||||
|         referer_url = request.META['HTTP_REFERER'] | ||||
| 
 | ||||
|         if vm_pricing is None: | ||||
|             vm_pricing_name_msg = _( | ||||
|                 "Incorrect pricing name. Please contact support" | ||||
|                 "{support_email}".format( | ||||
|                     support_email=settings.DCL_SUPPORT_FROM_ADDRESS | ||||
|                 ) | ||||
|             ) | ||||
|             messages.add_message( | ||||
|                 self.request, messages.ERROR, vm_pricing_name_msg, | ||||
|                 extra_tags='pricing' | ||||
|             ) | ||||
|             return HttpResponseRedirect(referer_url + "#order_form") | ||||
|         else: | ||||
|             vm_pricing_name = vm_pricing.name | ||||
| 
 | ||||
|         try: | ||||
|             cores = cores_field.clean(cores) | ||||
|         except ValidationError as err: | ||||
|  | @ -137,14 +156,21 @@ class IndexView(CreateView): | |||
|             ) | ||||
|             return HttpResponseRedirect(referer_url + "#order_form") | ||||
| 
 | ||||
|         amount_to_be_charged = get_vm_price( | ||||
|             cpu=cores, memory=memory, disk_size=storage | ||||
|         price, vat, vat_percent = get_vm_price_with_vat( | ||||
|             cpu=cores, | ||||
|             memory=memory, | ||||
|             ssd_size=storage, | ||||
|             pricing_name=vm_pricing_name | ||||
|         ) | ||||
|         specs = { | ||||
|             'cpu': cores, | ||||
|             'memory': memory, | ||||
|             'disk_size': storage, | ||||
|             'price': amount_to_be_charged | ||||
|             'price': price, | ||||
|             'vat': vat, | ||||
|             'vat_percent': vat_percent, | ||||
|             'total_price': price + vat, | ||||
|             'pricing_name': vm_pricing_name | ||||
|         } | ||||
|         request.session['specs'] = specs | ||||
|         request.session['template'] = template_data | ||||
|  | @ -218,7 +244,10 @@ class PaymentOrderView(FormView): | |||
|             'site_url': reverse('datacenterlight:index'), | ||||
|             'login_form': HostingUserLoginForm(prefix='login_form'), | ||||
|             'billing_address_form': billing_address_form, | ||||
|             'cms_integration': get_cms_integration('default') | ||||
|             'cms_integration': get_cms_integration('default'), | ||||
|             'vm_pricing': VMPricing.get_vm_pricing_by_name( | ||||
|                 self.request.session['specs']['pricing_name'] | ||||
|             ) | ||||
|         }) | ||||
|         return context | ||||
| 
 | ||||
|  | @ -391,7 +420,7 @@ class OrderConfirmationView(DetailView): | |||
|         cpu = specs.get('cpu') | ||||
|         memory = specs.get('memory') | ||||
|         disk_size = specs.get('disk_size') | ||||
|         amount_to_be_charged = specs.get('price') | ||||
|         amount_to_be_charged = specs.get('total_price') | ||||
|         plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu, | ||||
|                                                      memory=memory, | ||||
|                                                      disk_size=disk_size) | ||||
|  |  | |||
|  | @ -70,7 +70,7 @@ hr.small { | |||
|   } | ||||
|   .navbar-custom .navbar-brand { | ||||
|     color: white; | ||||
|     padding: 20px; | ||||
|     padding: 5px 20px; | ||||
|   } | ||||
|   .navbar-custom .navbar-brand:hover, | ||||
|   .navbar-custom .navbar-brand:focus { | ||||
|  |  | |||
|  | @ -516,7 +516,7 @@ META_INCLUDE_KEYWORDS = ["ungleich", "hosting", "switzerland", | |||
|                          "Schweiz", "Swiss", "cdist"] | ||||
| 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' | ||||
| 
 | ||||
| # PAYMENT | ||||
|  |  | |||
|  | @ -18,8 +18,8 @@ import debug_toolbar | |||
| 
 | ||||
| urlpatterns = [ | ||||
|     url(r'^index.html$', LandingView.as_view()), | ||||
|     url(r'^open_api/', include('opennebula_api.urls', | ||||
|                                namespace='opennebula_api')), | ||||
|     url(r'^open_api/', | ||||
|         include('opennebula_api.urls', namespace='opennebula_api')), | ||||
|     url(r'^railshosting/', RailsHostingView.as_view(), | ||||
|         name="rails.hosting"), | ||||
|     url(r'^nodehosting/', NodeJSHostingView.as_view(), | ||||
|  | @ -28,8 +28,7 @@ urlpatterns = [ | |||
|         name="django.hosting"), | ||||
|     url(r'^nosystemd/', include('nosystemd.urls', namespace="nosystemd")), | ||||
|     url(r'^taggit_autosuggest/', include('taggit_autosuggest.urls')), | ||||
|     url(r'^jsi18n/(?P<packages>\S+?)/$', | ||||
|         i18n.javascript_catalog), | ||||
|     url(r'^jsi18n/(?P<packages>\S+?)/$', i18n.javascript_catalog), | ||||
| ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) | ||||
| 
 | ||||
| urlpatterns += i18n_patterns( | ||||
|  | @ -45,29 +44,22 @@ urlpatterns += i18n_patterns( | |||
|     url(r'^admin/', include(admin.site.urls)), | ||||
|     url(r'^datacenterlight/', | ||||
|         include('datacenterlight.urls', namespace="datacenterlight")), | ||||
|     url(r'^hosting/', RedirectView.as_view( | ||||
|         url=reverse_lazy('hosting:login')), name='redirect_hosting_login'), | ||||
|     url(r'^hosting/', RedirectView.as_view(url=reverse_lazy('hosting:login')), | ||||
|         name='redirect_hosting_login'), | ||||
|     url(r'^alplora/', include('alplora.urls', namespace="alplora")), | ||||
|     url(r'^membership/', include(membership_urls)), | ||||
|     url(r'^digitalglarus/', include('digitalglarus.urls', | ||||
|                                     namespace="digitalglarus")), | ||||
|     url(r'^cms/blog/', | ||||
|         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'^digitalglarus/', | ||||
|         include('digitalglarus.urls', namespace="digitalglarus")), | ||||
|     url(r'^cms/blog/', 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]*)/$', | ||||
|         RedirectView.as_view(pattern_name='ungleich:post-detail')), | ||||
|     url(r'^blog/$', RedirectView.as_view( | ||||
|                 url=reverse_lazy('ungleich:post-list') | ||||
|             ), name='blog_list_view' | ||||
|         ), | ||||
|     url(r'^blog/$', | ||||
|         RedirectView.as_view(url=reverse_lazy('ungleich:post-list')), name='blog_list_view'), | ||||
|     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 | ||||
|         else LandingView.as_view()), | ||||
|     url(r'^', | ||||
|         include('ungleich_page.urls', | ||||
|                 namespace='ungleich_page'), | ||||
|         name='ungleich_page'), | ||||
|     url(r'^blog/', include('djangocms_blog.urls', namespace='djangocms_blog')), | ||||
|     url(r'^', include('ungleich_page.urls', namespace='ungleich_page')), | ||||
| ) | ||||
| 
 | ||||
| urlpatterns += [ | ||||
|  |  | |||
							
								
								
									
										23
									
								
								hosting/migrations/0044_hostingorder_vm_pricing.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								hosting/migrations/0044_hostingorder_vm_pricing.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| # Generated by Django 1.9.4 on 2018-04-16 00:22 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| import django.db.models.deletion | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('datacenterlight', '0019_auto_20180415_2236'), | ||||
|         ('hosting', '0043_vmdetail'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.AddField( | ||||
|             model_name='hostingorder', | ||||
|             name='vm_pricing', | ||||
|             field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='datacenterlight.VMPricing'), | ||||
|             preserve_default=False, | ||||
|         ), | ||||
|     ] | ||||
|  | @ -6,6 +6,8 @@ from django.db import models | |||
| from django.utils import timezone | ||||
| from django.utils.functional import cached_property | ||||
| from Crypto.PublicKey import RSA | ||||
| 
 | ||||
| from datacenterlight.models import VMPricing | ||||
| from membership.models import StripeCustomer, CustomUser | ||||
| from utils.models import BillingAddress | ||||
| from utils.mixins import AssignPermissionsMixin | ||||
|  | @ -53,6 +55,7 @@ class HostingOrder(AssignPermissionsMixin, models.Model): | |||
|     stripe_charge_id = models.CharField(max_length=100, null=True) | ||||
|     price = models.FloatField() | ||||
|     subscription_id = models.CharField(max_length=100, null=True) | ||||
|     vm_pricing = models.ForeignKey(VMPricing) | ||||
| 
 | ||||
|     permissions = ('view_hostingorder',) | ||||
| 
 | ||||
|  | @ -70,12 +73,13 @@ class HostingOrder(AssignPermissionsMixin, models.Model): | |||
| 
 | ||||
|     @classmethod | ||||
|     def create(cls, price=None, vm_id=None, customer=None, | ||||
|                billing_address=None): | ||||
|                billing_address=None, vm_pricing=None): | ||||
|         instance = cls.objects.create( | ||||
|             price=price, | ||||
|             vm_id=vm_id, | ||||
|             customer=customer, | ||||
|             billing_address=billing_address | ||||
|             billing_address=billing_address, | ||||
|             vm_pricing=vm_pricing | ||||
|         ) | ||||
|         instance.assign_permissions(customer.user) | ||||
|         return instance | ||||
|  |  | |||
|  | @ -127,9 +127,19 @@ | |||
|                             <span>{% trans "Disk space" %}: </span> | ||||
|                             <span class="pull-right">{{vm.disk_size}} GB</span> | ||||
|                         </p> | ||||
|                         {% if vm.vat > 0 %} | ||||
|                             <p> | ||||
|                                 <strong>{% trans "Subtotal" %}: </strong> | ||||
|                                 <span class="pull-right">{{vm.price|floatformat:2|intcomma}} CHF</span> | ||||
|                             </p> | ||||
|                             <p> | ||||
|                                 <span>{% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%): </span> | ||||
|                                 <span class="pull-right">{{vm.vat|floatformat:2|intcomma}} CHF</span> | ||||
|                             </p> | ||||
|                         {% endif %} | ||||
|                         <p> | ||||
|                             <span>{% trans "Total" %}</span> | ||||
|                             <span class="pull-right">{{vm.price|intcomma}} CHF</span> | ||||
|                             <strong>{% trans "Total" %}</strong> | ||||
|                             <span class="pull-right">{% if vm.total_price %}{{vm.total_price|floatformat:2|intcomma}}{% else %}{{vm.price|floatformat:2|intcomma}}{% endif %} CHF</span> | ||||
|                         </p> | ||||
|                     </div> | ||||
|                 </div> | ||||
|  |  | |||
|  | @ -29,7 +29,7 @@ | |||
|                 <tr> | ||||
|                     <td class="xs-td-inline" data-header="{% trans 'Order Nr.' %}">{{ order.id }}</td> | ||||
|                     <td class="xs-td-bighalf" data-header="{% trans 'Date' %}">{{ order.created_at | date:"M d, Y H:i" }}</td> | ||||
|                     <td class="xs-td-smallhalf" data-header="{% trans 'Amount' %}">{{ order.price|intcomma }}</td> | ||||
|                     <td class="xs-td-smallhalf" data-header="{% trans 'Amount' %}">{{ order.price|floatformat:2|intcomma }}</td> | ||||
|                     <td class="text-right last-td"> | ||||
|                         <a class="btn btn-order-detail" href="{% url 'hosting:orders' order.pk %}">{% trans 'See Invoice' %}</a> | ||||
|                     </td> | ||||
|  |  | |||
|  | @ -45,7 +45,7 @@ | |||
| 				<h2 class="vm-detail-title">{% trans "Billing" %} <img src="{% static 'hosting/img/billing.svg' %}" class="un-icon"></h2> | ||||
| 				<div class="vm-vmid"> | ||||
| 					<div class="vm-item-subtitle">{% trans "Current Pricing" %}</div> | ||||
| 					<div class="vm-item-lg">{{virtual_machine.price|floatformat|intcomma}} CHF/{% trans "Month" %}</div> | ||||
| 					<div class="vm-item-lg">{{order.price|floatformat:2|intcomma}} CHF/{% trans "Month" %}</div> | ||||
| 					<a class="btn btn-vm-invoice" href="{% url 'hosting:orders' order.pk %}">{% trans "See Invoice" %}</a> | ||||
| 				</div> | ||||
| 			</div> | ||||
|  |  | |||
|  | @ -43,7 +43,7 @@ from utils.forms import ( | |||
|     BillingAddressForm, PasswordResetRequestForm, UserBillingAddressForm, | ||||
|     ResendActivationEmailForm | ||||
| ) | ||||
| from utils.hosting_utils import get_vm_price | ||||
| from utils.hosting_utils import get_vm_price, get_vm_price_with_vat | ||||
| from utils.mailer import BaseEmail | ||||
| from utils.stripe_utils import StripeUtils | ||||
| from utils.tasks import send_plain_email_task | ||||
|  | @ -750,11 +750,17 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): | |||
|                 context['vm'] = vm_detail.__dict__ | ||||
|                 context['vm']['name'] = '{}-{}'.format( | ||||
|                     context['vm']['configuration'], context['vm']['vm_id']) | ||||
|                 context['vm']['price'] = get_vm_price( | ||||
|                 price, vat, vat_percent = get_vm_price_with_vat( | ||||
|                     cpu=context['vm']['cores'], | ||||
|                     disk_size=context['vm']['disk_size'], | ||||
|                     memory=context['vm']['memory'] | ||||
|                     ssd_size=context['vm']['disk_size'], | ||||
|                     memory=context['vm']['memory'], | ||||
|                     pricing_name=(obj.vm_pricing.name | ||||
|                                   if obj.vm_pricing else 'default') | ||||
|                 ) | ||||
|                 context['vm']['vat'] = vat | ||||
|                 context['vm']['price'] = price | ||||
|                 context['vm']['vat_percent'] = vat_percent | ||||
|                 context['vm']['total_price'] = price + vat | ||||
|                 context['subscription_end_date'] = vm_detail.end_date() | ||||
|             except VMDetail.DoesNotExist: | ||||
|                 try: | ||||
|  | @ -763,6 +769,17 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): | |||
|                     ) | ||||
|                     vm = manager.get_vm(obj.vm_id) | ||||
|                     context['vm'] = VirtualMachineSerializer(vm).data | ||||
|                     price, vat, vat_percent = get_vm_price_with_vat( | ||||
|                         cpu=context['vm']['cores'], | ||||
|                         ssd_size=context['vm']['disk_size'], | ||||
|                         memory=context['vm']['memory'], | ||||
|                         pricing_name=(obj.vm_pricing.name | ||||
|                                       if obj.vm_pricing else 'default') | ||||
|                     ) | ||||
|                     context['vm']['vat'] = vat | ||||
|                     context['vm']['price'] = price | ||||
|                     context['vm']['vat_percent'] = vat_percent | ||||
|                     context['vm']['total_price'] = price + vat | ||||
|                 except WrongIdError: | ||||
|                     messages.error( | ||||
|                         self.request, | ||||
|  | @ -1094,7 +1111,8 @@ class VirtualMachineView(LoginRequiredMixin, View): | |||
|             context = { | ||||
|                 'virtual_machine': serializer.data, | ||||
|                 'order': HostingOrder.objects.get( | ||||
|                     vm_id=serializer.data['vm_id']) | ||||
|                     vm_id=serializer.data['vm_id'] | ||||
|                 ) | ||||
|             } | ||||
|         except Exception as ex: | ||||
|             logger.debug("Exception generated {}".format(str(ex))) | ||||
|  |  | |||
|  | @ -5,29 +5,34 @@ | |||
|     <!-- Brand and toggle get grouped for better mobile display --> | ||||
|     <div class="navbar-header page-scroll"> | ||||
|       <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="icon-bar"></span> | ||||
| 	<span class="icon-bar"></span> | ||||
| 	<span class="icon-bar"></span> | ||||
|     	 <span class="sr-only">Toggle navigation</span> | ||||
|       	<span class="icon-bar"></span> | ||||
|       	<span class="icon-bar"></span> | ||||
|       	<span class="icon-bar"></span> | ||||
|       </button> | ||||
|       <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> | ||||
|     </div> | ||||
| 
 | ||||
|     <!-- Collect the nav links, forms, and other content for toggling --> | ||||
|     <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> | ||||
|       <ul class="nav navbar-nav navbar-right"> | ||||
| 	{% for child in children %} | ||||
| 	<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="{{ child.attr.redirect_url|default:child.get_absolute_url }}">{{ child.get_menu_title }}</a> | ||||
| 	  {% if child.children %} | ||||
| 	  <ul> | ||||
| 	    {% show_menu from_level to_level extra_inactive extra_active template "" "" child %} | ||||
| 	  </ul> | ||||
| 	  {% endif %} | ||||
| 	</li> | ||||
| 	{% endfor %} | ||||
|         <li> | ||||
|           <a href="{% url 'djangocms_blog:posts-latest' %}">Ungleich Blog</a> | ||||
|         </li> | ||||
|         {% comment %} | ||||
|         	{% for child in children %} | ||||
|           	<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="{{ child.attr.redirect_url|default:child.get_absolute_url }}">{{ child.get_menu_title }}</a> | ||||
|           	  {% if child.children %} | ||||
|           	  <ul> | ||||
|           	    {% show_menu from_level to_level extra_inactive extra_active template "" "" child %} | ||||
|           	  </ul> | ||||
|           	  {% endif %} | ||||
|           	</li> | ||||
|         	{% endfor %} | ||||
|         {% endcomment %} | ||||
|       </ul> | ||||
|     </div> | ||||
|     <!-- /.navbar-collapse --> | ||||
|  |  | |||
|  | @ -1,6 +1,8 @@ | |||
| import decimal | ||||
| import logging | ||||
| from oca.pool import WrongIdError | ||||
| 
 | ||||
| from datacenterlight.models import VMPricing | ||||
| from hosting.models import UserHostingKey, VMDetail | ||||
| from opennebula_api.serializers import VirtualMachineSerializer | ||||
| 
 | ||||
|  | @ -49,14 +51,74 @@ def get_or_create_vm_detail(user, manager, vm_id): | |||
|     return vm_detail_obj | ||||
| 
 | ||||
| 
 | ||||
| def get_vm_price(cpu, memory, disk_size): | ||||
| def get_vm_price(cpu, memory, disk_size, hdd_size=0, pricing_name='default'): | ||||
|     """ | ||||
|     A helper function that computes price of a VM from given cpu, ram and | ||||
|     ssd parameters | ||||
| 
 | ||||
|     :param cpu: Number of cores of the VM | ||||
|     :param memory: RAM of the VM | ||||
|     :param disk_size: Disk space of the VM | ||||
|     :param disk_size: Disk space of the VM (SSD) | ||||
|     :param hdd_size: The HDD size | ||||
|     :param pricing_name: The pricing name to be used | ||||
|     :return: The price of the VM | ||||
|     """ | ||||
|     return (cpu * 5) + (memory * 2) + (disk_size * 0.6) | ||||
|     try: | ||||
|         pricing = VMPricing.objects.get(name=pricing_name) | ||||
|     except Exception as ex: | ||||
|         logger.error( | ||||
|             "Error getting VMPricing object for {pricing_name}." | ||||
|             "Details: {details}".format( | ||||
|                 pricing_name=pricing_name, details=str(ex) | ||||
|             ) | ||||
|         ) | ||||
|         return None | ||||
|     price = ((decimal.Decimal(cpu) * pricing.cores_unit_price) + | ||||
|              (decimal.Decimal(memory) * pricing.ram_unit_price) + | ||||
|              (decimal.Decimal(disk_size) * pricing.ssd_unit_price) + | ||||
|              (decimal.Decimal(hdd_size) * pricing.hdd_unit_price)) | ||||
|     cents = decimal.Decimal('.01') | ||||
|     price = price.quantize(cents, decimal.ROUND_HALF_UP) | ||||
|     return float(price) | ||||
| 
 | ||||
| 
 | ||||
| def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0, | ||||
|                           pricing_name='default'): | ||||
|     """ | ||||
|     A helper function that computes price of a VM from given cpu, ram and | ||||
|     ssd, hdd and the pricing parameters | ||||
| 
 | ||||
|     :param cpu: Number of cores of the VM | ||||
|     :param memory: RAM of the VM | ||||
|     :param ssd_size: Disk space of the VM (SSD) | ||||
|     :param hdd_size: The HDD size | ||||
|     :param pricing_name: The pricing name to be used | ||||
|     :return: The a tuple containing the price of the VM, the VAT and the | ||||
|              VAT percentage | ||||
|     """ | ||||
|     try: | ||||
|         pricing = VMPricing.objects.get(name=pricing_name) | ||||
|     except Exception as ex: | ||||
|         logger.error( | ||||
|             "Error getting VMPricing object for {pricing_name}." | ||||
|             "Details: {details}".format( | ||||
|                 pricing_name=pricing_name, details=str(ex) | ||||
|             ) | ||||
|         ) | ||||
|         return None | ||||
| 
 | ||||
|     price = ((decimal.Decimal(cpu) * pricing.cores_unit_price) + | ||||
|              (decimal.Decimal(memory) * pricing.ram_unit_price) + | ||||
|              (decimal.Decimal(ssd_size) * pricing.ssd_unit_price) + | ||||
|              (decimal.Decimal(hdd_size) * pricing.hdd_unit_price)) | ||||
|     if pricing.vat_inclusive: | ||||
|         vat = decimal.Decimal(0) | ||||
|         vat_percent = decimal.Decimal(0) | ||||
|     else: | ||||
|         vat = price * pricing.vat_percentage * decimal.Decimal(0.01) | ||||
|         vat_percent = pricing.vat_percentage | ||||
| 
 | ||||
|     cents = decimal.Decimal('.01') | ||||
|     price = price.quantize(cents, decimal.ROUND_HALF_UP) | ||||
|     vat = vat.quantize(cents, decimal.ROUND_HALF_UP) | ||||
|     return float(price), float(vat), float(vat_percent) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue