Merge remote-tracking branch 'ungleich/master'
This commit is contained in:
		
				commit
				
					
						71a37972d3
					
				
			
		
					 21 changed files with 504 additions and 118 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -42,3 +42,4 @@ secret-key | |||
| 
 | ||||
| # to keep empty dirs | ||||
| !.gitkeep | ||||
| *.orig | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| from django.contrib import admin | ||||
| from cms.admin.placeholderadmin import PlaceholderAdminMixin | ||||
| from .cms_models import CMSIntegration | ||||
| from .models import VMPricing | ||||
| 
 | ||||
| 
 | ||||
| class CMSIntegrationAdmin(PlaceholderAdminMixin, admin.ModelAdmin): | ||||
|  | @ -8,3 +9,4 @@ class CMSIntegrationAdmin(PlaceholderAdminMixin, admin.ModelAdmin): | |||
| 
 | ||||
| 
 | ||||
| admin.site.register(CMSIntegration, CMSIntegrationAdmin) | ||||
| admin.site.register(VMPricing) | ||||
|  |  | |||
|  | @ -6,6 +6,8 @@ from django.utils.safestring import mark_safe | |||
| from djangocms_text_ckeditor.fields import HTMLField | ||||
| from filer.fields.image import FilerImageField | ||||
| 
 | ||||
| from datacenterlight.models import VMPricing | ||||
| 
 | ||||
| 
 | ||||
| class CMSIntegration(models.Model): | ||||
|     name = models.CharField( | ||||
|  | @ -275,3 +277,12 @@ class DCLSectionPromoPluginModel(CMSPlugin): | |||
|         if self.background_image: | ||||
|             extra_classes += ' promo-with-bg' | ||||
|         return extra_classes | ||||
| 
 | ||||
| 
 | ||||
| class DCLCustomPricingModel(CMSPlugin): | ||||
|     pricing = models.ForeignKey( | ||||
|         VMPricing, | ||||
|         related_name="dcl_custom_pricing_vm_pricing", | ||||
|         help_text='Choose a pricing that will be associated with this ' | ||||
|                   'Calculator' | ||||
|     ) | ||||
|  |  | |||
|  | @ -6,9 +6,9 @@ from .cms_models import ( | |||
|     DCLFooterPluginModel, DCLLinkPluginModel, DCLNavbarDropdownPluginModel, | ||||
|     DCLSectionIconPluginModel, DCLSectionImagePluginModel, | ||||
|     DCLSectionPluginModel, DCLNavbarPluginModel, | ||||
|     DCLSectionPromoPluginModel | ||||
|     DCLSectionPromoPluginModel, DCLCustomPricingModel | ||||
| ) | ||||
| from .models import VMTemplate | ||||
| from .models import VMTemplate, VMPricing | ||||
| 
 | ||||
| 
 | ||||
| @plugin_pool.register_plugin | ||||
|  | @ -75,13 +75,13 @@ class DCLSectionPromoPlugin(CMSPluginBase): | |||
| @plugin_pool.register_plugin | ||||
| class DCLCalculatorPlugin(CMSPluginBase): | ||||
|     module = "Datacenterlight" | ||||
|     name = "DCL Calculator Plugin" | ||||
|     name = "DCL Calculator Section Plugin" | ||||
|     model = DCLSectionPluginModel | ||||
|     render_template = "datacenterlight/cms/calculator.html" | ||||
|     cache = False | ||||
|     allow_children = True | ||||
|     child_classes = [ | ||||
|         'DCLSectionPromoPlugin', 'UngleichHTMLPlugin' | ||||
|         'DCLSectionPromoPlugin', 'UngleichHTMLPlugin', 'DCLCustomPricingPlugin' | ||||
|     ] | ||||
| 
 | ||||
|     def render(self, context, instance, placeholder): | ||||
|  | @ -89,15 +89,38 @@ class DCLCalculatorPlugin(CMSPluginBase): | |||
|             context, instance, placeholder | ||||
|         ) | ||||
|         context['templates'] = VMTemplate.objects.all() | ||||
|         context['children_to_side'] = [] | ||||
|         context['children_to_content'] = [] | ||||
|         pricing_plugin_model = None | ||||
|         if instance.child_plugin_instances is not None: | ||||
|             context['children_to_content'].extend( | ||||
|                 instance.child_plugin_instances | ||||
|             ) | ||||
|             for child in instance.child_plugin_instances: | ||||
|                 if child.__class__.__name__ == 'DCLCustomPricingModel': | ||||
|                     # The second clause is just to make sure we pick up the | ||||
|                     # most recent CustomPricing, if more than one is present | ||||
|                     if (pricing_plugin_model is None or child.pricing_id > | ||||
|                             pricing_plugin_model.model.pricing_id): | ||||
|                         pricing_plugin_model = child | ||||
| 
 | ||||
|         if pricing_plugin_model: | ||||
|             context['vm_pricing'] = VMPricing.get_vm_pricing_by_name( | ||||
|                 name=pricing_plugin_model.pricing.name | ||||
|             ) | ||||
|         else: | ||||
|             context['vm_pricing'] = VMPricing.get_default_pricing() | ||||
| 
 | ||||
|         return context | ||||
| 
 | ||||
| 
 | ||||
| @plugin_pool.register_plugin | ||||
| class DCLCustomPricingPlugin(CMSPluginBase): | ||||
|     module = "Datacenterlight" | ||||
|     name = "DCL Custom Pricing Plugin" | ||||
|     model = DCLCustomPricingModel | ||||
|     render_plugin = False | ||||
| 
 | ||||
| 
 | ||||
| @plugin_pool.register_plugin | ||||
| class DCLBannerListPlugin(CMSPluginBase): | ||||
|     module = "Datacenterlight" | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ msgid "" | |||
| msgstr "" | ||||
| "Project-Id-Version: PACKAGE VERSION\n" | ||||
| "Report-Msgid-Bugs-To: \n" | ||||
| "POT-Creation-Date: 2018-03-30 21:29+0000\n" | ||||
| "POT-Creation-Date: 2018-04-17 19:26+0000\n" | ||||
| "PO-Revision-Date: 2018-03-30 23:22+0000\n" | ||||
| "Last-Translator: b'Anonymous User <coder.purple+25@gmail.com>'\n" | ||||
| "Language-Team: LANGUAGE <LL@li.org>\n" | ||||
|  | @ -72,9 +72,9 @@ msgstr "Data Center Light Account Aktivierung" | |||
| 
 | ||||
| #, python-format | ||||
| msgid "" | ||||
| "You can activate your Data Center Light account by clicking <a " | ||||
| "href=\"%(base_url)s%(activation_link)s\" style=\"text-decoration: none; " | ||||
| "color: #4382c8; font-weight: 400;\">here</a>." | ||||
| "You can activate your Data Center Light account by clicking <a href=" | ||||
| "\"%(base_url)s%(activation_link)s\" style=\"text-decoration: none; color: " | ||||
| "#4382c8; font-weight: 400;\">here</a>." | ||||
| msgstr "" | ||||
| "Klicke <a href=\"%(base_url)s%(activation_link)s\"style=\"text-decoration: " | ||||
| "none; color: #4382c8; font-weight: 400;\">hier</a> um deinen Data Center " | ||||
|  | @ -97,12 +97,13 @@ msgstr "Deine E-Mail-Adresse" | |||
| msgid "Password" | ||||
| msgstr "Passwort" | ||||
| 
 | ||||
| #, python-format | ||||
| msgid "" | ||||
| "You can reset your password <a href=\"%(base_url)s%(reset_password_url)s\" " | ||||
| "style=\"text-decoration: none; color: #4382c8; font-weight: 400;\">here</a>." | ||||
| msgstr "" | ||||
| "Du kannst dein Passwort <a href=\"%(base_url)s%(reset_password_url)s\" " | ||||
| "style=\"text-decoration: none; color: #4382c8; font-weight: 400;\">hier</a> " | ||||
| "Du kannst dein Passwort <a href=\"%(base_url)s%(reset_password_url)s\" style=" | ||||
| "\"text-decoration: none; color: #4382c8; font-weight: 400;\">hier</a> " | ||||
| "zurücksetzen." | ||||
| 
 | ||||
| msgid "Your Data Center Light Team" | ||||
|  | @ -160,21 +161,6 @@ msgstr "Weiter" | |||
| msgid "Home" | ||||
| msgstr "Home" | ||||
| 
 | ||||
| msgid "Highlights" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Scale out" | ||||
| msgstr "Skalierung" | ||||
| 
 | ||||
| msgid "Reliable and light" | ||||
| msgstr "Zuverlässig und leicht" | ||||
| 
 | ||||
| msgid "Pricing" | ||||
| msgstr "Preise" | ||||
| 
 | ||||
| msgid "Order VM" | ||||
| msgstr "VM bestellen" | ||||
| 
 | ||||
| msgid "Contact" | ||||
| msgstr "Kontakt" | ||||
| 
 | ||||
|  | @ -184,6 +170,9 @@ msgstr "Nutzungsbedingungen" | |||
| msgid "Finally, an affordable VM hosting in Switzerland!" | ||||
| msgstr "Endlich: bezahlbares VM Hosting in der Schweiz" | ||||
| 
 | ||||
| msgid "Highlights" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "I want it!" | ||||
| msgstr "Das will ich haben!" | ||||
| 
 | ||||
|  | @ -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,13 +318,13 @@ 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, " | ||||
|  | @ -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" | ||||
|  | @ -459,21 +461,21 @@ msgid "" | |||
| "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" | ||||
| 
 | ||||
|  | @ -520,6 +526,12 @@ msgstr "" | |||
| "Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du " | ||||
| "auf sie zugreifen kannst." | ||||
| 
 | ||||
| #~ msgid "Pricing" | ||||
| #~ msgstr "Preise" | ||||
| 
 | ||||
| #~ msgid "Order VM" | ||||
| #~ msgstr "VM bestellen" | ||||
| 
 | ||||
| #~ msgid "Enter name" | ||||
| #~ msgstr "Name" | ||||
| 
 | ||||
|  | @ -533,18 +545,19 @@ msgstr "" | |||
| #~ msgstr "Anfrage verschickt" | ||||
| 
 | ||||
| #~ msgid "" | ||||
| #~ "Thank you for your subscription! You will receive a confirmation mail from " | ||||
| #~ "our team" | ||||
| #~ "Thank you for your subscription! You will receive a confirmation mail " | ||||
| #~ "from our team" | ||||
| #~ msgstr "" | ||||
| #~ "Vielen dank für Ihre Anmeldung. Sie erhalten in kürze eine Bestätigungsmail " | ||||
| #~ "von unserem Team" | ||||
| #~ "Vielen dank für Ihre Anmeldung. Sie erhalten in kürze eine " | ||||
| #~ "Bestätigungsmail von unserem Team" | ||||
| 
 | ||||
| #~ msgid "Thank you for your request." | ||||
| #~ msgstr "Vielen Dank für Deine Anfrage." | ||||
| 
 | ||||
| #~ msgid "You are one step away from being our beta tester!" | ||||
| #~ msgstr "" | ||||
| #~ "Sie sind nur noch einen Schritt davon entfernt, unser Beta-Tester zu werden!" | ||||
| #~ "Sie sind nur noch einen Schritt davon entfernt, unser Beta-Tester zu " | ||||
| #~ "werden!" | ||||
| 
 | ||||
| #~ msgid "" | ||||
| #~ "Currently we are running our tests to make sure everything runs perfectly." | ||||
|  | @ -553,8 +566,8 @@ msgstr "" | |||
| #~ "sicherzustellen." | ||||
| 
 | ||||
| #~ msgid "" | ||||
| #~ "In the meantime, we would like to ask you a little patience<br/> until our " | ||||
| #~ "team contacts you with beta access." | ||||
| #~ "In the meantime, we would like to ask you a little patience<br/> until " | ||||
| #~ "our team contacts you with beta access." | ||||
| #~ msgstr "" | ||||
| #~ "Wir werden dann sobald als möglich Ihren Beta-Zugang erstellen und Sie " | ||||
| #~ "daraufhin kontaktieren.Bis dahin bitten wir Sie um etwas Geduld." | ||||
|  | @ -564,8 +577,8 @@ msgstr "" | |||
| 
 | ||||
| #~ msgid "Thank you for order! Our team will contact you via email" | ||||
| #~ msgstr "" | ||||
| #~ "Vielen Dank für die Bestellung. Unser Team setzt sich sobald wie möglich mit" | ||||
| #~ " Dir via E-Mail in Verbindung." | ||||
| #~ "Vielen Dank für die Bestellung. Unser Team setzt sich sobald wie möglich " | ||||
| #~ "mit Dir via E-Mail in Verbindung." | ||||
| 
 | ||||
| #~ msgid "Affordable VM hosting based in Switzerland" | ||||
| #~ msgstr "Bezahlbares VM Hosting in der Schweiz" | ||||
|  | @ -581,18 +594,18 @@ msgstr "" | |||
| 
 | ||||
| #~ msgid "" | ||||
| #~ "Our VMs are hosted in Glarus, Switzerland, and our website is currently " | ||||
| #~ "running in BETA mode. If you want more information that you did not find on " | ||||
| #~ "our website, or if your order is more detailed, or if you encounter any " | ||||
| #~ "technical hiccups, please contact us at support@datacenterlight.ch, our team" | ||||
| #~ " will get in touch with you asap." | ||||
| #~ "running in BETA mode. If you want more information that you did not find " | ||||
| #~ "on our website, or if your order is more detailed, or if you encounter " | ||||
| #~ "any technical hiccups, please contact us at support@datacenterlight.ch, " | ||||
| #~ "our team will get in touch with you asap." | ||||
| #~ msgstr "" | ||||
| #~ "Unsere VMs werden in der Schweiz im Kanton Glarus gehostet und befinden sich" | ||||
| #~ " zur Zeit noch in der BETA-Phase. Möchtest du mehr über uns erfahren und " | ||||
| #~ "hast auf unserer Website nicht genügend Informationen gefunden? Möchtest " | ||||
| #~ "eine detailliertere Bestellung aufgeben? Bist du auf technische Probleme " | ||||
| #~ "gestossen, die du uns mitteilen möchtest? Dann zögere nicht und kontaktiere " | ||||
| #~ "uns unter support@datacenterlight.ch. Unser Team wird sich umgehend um dein " | ||||
| #~ "Anliegen kümmern!" | ||||
| #~ "Unsere VMs werden in der Schweiz im Kanton Glarus gehostet und befinden " | ||||
| #~ "sich zur Zeit noch in der BETA-Phase. Möchtest du mehr über uns erfahren " | ||||
| #~ "und hast auf unserer Website nicht genügend Informationen gefunden? " | ||||
| #~ "Möchtest eine detailliertere Bestellung aufgeben? Bist du auf technische " | ||||
| #~ "Probleme gestossen, die du uns mitteilen möchtest? Dann zögere nicht und " | ||||
| #~ "kontaktiere uns unter support@datacenterlight.ch. Unser Team wird sich " | ||||
| #~ "umgehend um dein Anliegen kümmern!" | ||||
| 
 | ||||
| #~ msgid "is not a proper name" | ||||
| #~ msgstr "ist kein gültiger Name" | ||||
|  | @ -610,12 +623,14 @@ msgstr "" | |||
| #~ "\n" | ||||
| #~ "Hi,\n" | ||||
| #~ "\n" | ||||
| #~ "You can activate your %(dcl_text)s account by clicking here %(base_url)s%(activation_link)s\n" | ||||
| #~ "You can activate your %(dcl_text)s account by clicking here %(base_url)s" | ||||
| #~ "%(activation_link)s\n" | ||||
| #~ msgstr "" | ||||
| #~ "\n" | ||||
| #~ "Hallo,\n" | ||||
| #~ "\n" | ||||
| #~ "Du kannst deinen %(dcl_text)s Account aktivieren, indem du hier klickst %(base_url)s%(activation_link)s\n" | ||||
| #~ "Du kannst deinen %(dcl_text)s Account aktivieren, indem du hier klickst " | ||||
| #~ "%(base_url)s%(activation_link)s\n" | ||||
| 
 | ||||
| #~ msgid "Your" | ||||
| #~ msgstr "Dein" | ||||
|  | @ -650,12 +665,14 @@ msgstr "" | |||
| #~ msgid "I want to have it!" | ||||
| #~ msgstr "Das möchte ich haben!" | ||||
| 
 | ||||
| #~ msgid "Reuse existing factory halls intead of building an expensive building." | ||||
| #~ msgid "" | ||||
| #~ "Reuse existing factory halls intead of building an expensive building." | ||||
| #~ msgstr "" | ||||
| #~ "Nachhaltigkeit: Wiederverwendung ehemaliger Fabrikhallen an Stelle der " | ||||
| #~ "Errichtung eines neuen Gebäudes" | ||||
| 
 | ||||
| #~ msgid "Being creative, using modern and alternative design for a datacenter." | ||||
| #~ msgid "" | ||||
| #~ "Being creative, using modern and alternative design for a datacenter." | ||||
| #~ msgstr "" | ||||
| #~ "Kreativität: Verwendung eines modernen und alternativen Designs für unser " | ||||
| #~ "Datencenter" | ||||
|  | @ -678,8 +695,8 @@ msgstr "" | |||
| #~ msgstr "Standort des Datacenters ist in der Schweiz" | ||||
| 
 | ||||
| #~ msgid "" | ||||
| #~ " WARNING: We are currently running in BETA mode. We hope you won't encounter" | ||||
| #~ " any hiccups, but if you do, please let us know at " | ||||
| #~ " WARNING: We are currently running in BETA mode. We hope you won't " | ||||
| #~ "encounter any hiccups, but if you do, please let us know at " | ||||
| #~ "support@datacenterlight.ch" | ||||
| #~ msgstr "" | ||||
| #~ " Achtung: Wir befinden uns zurzeit im Beta-Release. Wir hoffen, dass Sie " | ||||
|  | @ -693,8 +710,8 @@ msgstr "" | |||
| #~ msgstr "Unser Versprechen" | ||||
| 
 | ||||
| #~ msgid "" | ||||
| #~ "Instead of creating an expensive SLA for availability, we promise that we do" | ||||
| #~ " our best to run things as smooth as possible." | ||||
| #~ "Instead of creating an expensive SLA for availability, we promise that we " | ||||
| #~ "do our best to run things as smooth as possible." | ||||
| #~ msgstr "" | ||||
| #~ "Anstatt eines SLAs (Service Levle Agreements) zu vereinbaren,setzen wir " | ||||
| #~ "unsere persönliche Arbeitskraft ein, um Ihnen ein sorgenfreiesHosting zu " | ||||
|  |  | |||
|  | @ -0,0 +1,36 @@ | |||
| from django.core.management.base import BaseCommand | ||||
| 
 | ||||
| from datacenterlight.models import VMPricing | ||||
| 
 | ||||
| 
 | ||||
| class Command(BaseCommand): | ||||
|     help = '''Creates default VMPricing object''' | ||||
|     DEFAULT_VMPRICING_NAME = 'default' | ||||
| 
 | ||||
|     def handle(self, *args, **options): | ||||
|         self.create_default_vm_pricing() | ||||
| 
 | ||||
|     def create_default_vm_pricing(self): | ||||
|         obj, created = VMPricing.objects.get_or_create( | ||||
|             name=self.DEFAULT_VMPRICING_NAME, | ||||
|             defaults={ | ||||
|                 "vat_inclusive": True, | ||||
|                 "cores_unit_price": 5, | ||||
|                 "ram_unit_price": 2, | ||||
|                 "ssd_unit_price": 0.6, | ||||
|                 "hdd_unit_price": 0.01 | ||||
|             } | ||||
|         ) | ||||
| 
 | ||||
|         if created: | ||||
|             print( | ||||
|                 'Successfully created {} VMPricing object'.format( | ||||
|                     self.DEFAULT_VMPRICING_NAME | ||||
|                 ) | ||||
|             ) | ||||
|         else: | ||||
|             print( | ||||
|                 '{} VMPricing exists already.'.format( | ||||
|                     self.DEFAULT_VMPRICING_NAME | ||||
|                 ) | ||||
|             ) | ||||
							
								
								
									
										45
									
								
								datacenterlight/migrations/0019_auto_20180415_2236.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								datacenterlight/migrations/0019_auto_20180415_2236.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| # Generated by Django 1.9.4 on 2018-04-15 22:36 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| import django.db.models.deletion | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('cms', '0014_auto_20160404_1908'), | ||||
|         ('datacenterlight', '0018_auto_20180403_1930'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.CreateModel( | ||||
|             name='DCLCustomPricingModel', | ||||
|             fields=[ | ||||
|                 ('cmsplugin_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='cms.CMSPlugin')), | ||||
|             ], | ||||
|             options={ | ||||
|                 'abstract': False, | ||||
|             }, | ||||
|             bases=('cms.cmsplugin',), | ||||
|         ), | ||||
|         migrations.CreateModel( | ||||
|             name='VMPricing', | ||||
|             fields=[ | ||||
|                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||||
|                 ('name', models.CharField(max_length=255, unique=True)), | ||||
|                 ('vat_inclusive', models.BooleanField(default=True)), | ||||
|                 ('vat_percentage', models.DecimalField(blank=True, decimal_places=5, default=0, max_digits=7)), | ||||
|                 ('cores_unit_price', models.DecimalField(decimal_places=5, default=0, max_digits=7)), | ||||
|                 ('ram_unit_price', models.DecimalField(decimal_places=5, default=0, max_digits=7)), | ||||
|                 ('ssd_unit_price', models.DecimalField(decimal_places=5, default=0, max_digits=7)), | ||||
|                 ('hdd_unit_price', models.DecimalField(decimal_places=6, default=0, max_digits=7)), | ||||
|             ], | ||||
|         ), | ||||
|         migrations.AddField( | ||||
|             model_name='dclcustompricingmodel', | ||||
|             name='pricing', | ||||
|             field=models.ForeignKey(help_text='Choose a pricing that will be associated with this Calculator', on_delete=django.db.models.deletion.CASCADE, related_name='dcl_custom_pricing_vm_pricing', to='datacenterlight.VMPricing'), | ||||
|         ), | ||||
|     ] | ||||
|  | @ -1,5 +1,9 @@ | |||
| import logging | ||||
| 
 | ||||
| from django.db import models | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| 
 | ||||
| class VMTemplate(models.Model): | ||||
|     name = models.CharField(max_length=50) | ||||
|  | @ -12,6 +16,59 @@ class VMTemplate(models.Model): | |||
|         return vm_template | ||||
| 
 | ||||
| 
 | ||||
| class VMPricing(models.Model): | ||||
|     name = models.CharField(max_length=255, unique=True) | ||||
|     vat_inclusive = models.BooleanField(default=True) | ||||
|     vat_percentage = models.DecimalField( | ||||
|         max_digits=7, decimal_places=5, blank=True, default=0 | ||||
|     ) | ||||
|     cores_unit_price = models.DecimalField( | ||||
|         max_digits=7, decimal_places=5, default=0 | ||||
|     ) | ||||
|     ram_unit_price = models.DecimalField( | ||||
|         max_digits=7, decimal_places=5, default=0 | ||||
|     ) | ||||
|     ssd_unit_price = models.DecimalField( | ||||
|         max_digits=7, decimal_places=5, default=0 | ||||
|     ) | ||||
|     hdd_unit_price = models.DecimalField( | ||||
|         max_digits=7, decimal_places=6, default=0 | ||||
|     ) | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         return self.name + ' => ' + ' - '.join([ | ||||
|             '{}/Core'.format(self.cores_unit_price.normalize()), | ||||
|             '{}/GB RAM'.format(self.ram_unit_price.normalize()), | ||||
|             '{}/GB SSD'.format(self.ssd_unit_price.normalize()), | ||||
|             '{}/GB HDD'.format(self.hdd_unit_price.normalize()), | ||||
|             '{}% VAT'.format(self.vat_percentage.normalize()) | ||||
|             if not self.vat_inclusive else 'VAT-Incl', ] | ||||
|         ) | ||||
| 
 | ||||
|     @classmethod | ||||
|     def get_vm_pricing_by_name(cls, name): | ||||
|         try: | ||||
|             pricing = VMPricing.objects.get(name=name) | ||||
|         except Exception as e: | ||||
|             logger.error( | ||||
|                 "Error getting VMPricing with name {name}. " | ||||
|                 "Details: {details}. Attempting to return default" | ||||
|                 "pricing.".format(name=name, details=str(e)) | ||||
|             ) | ||||
|             pricing = VMPricing.get_default_pricing() | ||||
|         return pricing | ||||
| 
 | ||||
|     @classmethod | ||||
|     def get_default_pricing(cls): | ||||
|         """ Returns the default pricing or None """ | ||||
|         try: | ||||
|             default_pricing = VMPricing.objects.get(name='default') | ||||
|         except Exception as e: | ||||
|             logger.error(str(e)) | ||||
|             default_pricing = None | ||||
|         return default_pricing | ||||
| 
 | ||||
| 
 | ||||
| class StripePlan(models.Model): | ||||
|     """ | ||||
|     A model to store Data Center Light's created Stripe plans | ||||
|  |  | |||
|  | @ -171,7 +171,18 @@ | |||
|     } | ||||
| 
 | ||||
|     function _calcPricing() { | ||||
|         var total = (cardPricing['cpu'].value * 5) + (2 * cardPricing['ram'].value) + (0.6 * cardPricing['storage'].value); | ||||
|         if(typeof window.coresUnitPrice === 'undefined'){ | ||||
|             window.coresUnitPrice = 5; | ||||
|         } | ||||
|         if(typeof window.ramUnitPrice === 'undefined'){ | ||||
|             window.coresUnitPrice = 2; | ||||
|         } | ||||
|         if(typeof window.ssdUnitPrice === 'undefined'){ | ||||
|             window.ssdUnitPrice = 0.6; | ||||
|         } | ||||
|         var total = (cardPricing['cpu'].value * window.coresUnitPrice) + | ||||
|                     (cardPricing['ram'].value * window.ramUnitPrice) + | ||||
|                     (cardPricing['storage'].value * window.ssdUnitPrice); | ||||
|         total = parseFloat(total.toFixed(2)); | ||||
|         $("#total").text(total); | ||||
|     } | ||||
|  |  | |||
|  | @ -19,6 +19,8 @@ from utils.forms import UserBillingAddressForm | |||
| from utils.mailer import BaseEmail | ||||
| from utils.models import BillingAddress | ||||
| 
 | ||||
| from .models import VMPricing | ||||
| 
 | ||||
| logger = get_task_logger(__name__) | ||||
| 
 | ||||
| 
 | ||||
|  | @ -56,7 +58,8 @@ def create_vm_task(self, vm_template_id, user, specs, template, | |||
|         "Running create_vm_task on {}".format(current_task.request.hostname)) | ||||
|     vm_id = None | ||||
|     try: | ||||
|         final_price = specs.get('price') | ||||
|         final_price = (specs.get('total_price') if 'total_price' in specs | ||||
|                        else specs.get('price')) | ||||
|         billing_address = BillingAddress( | ||||
|             cardholder_name=billing_address_data['cardholder_name'], | ||||
|             street_address=billing_address_data['street_address'], | ||||
|  | @ -94,17 +97,22 @@ def create_vm_task(self, vm_template_id, user, specs, template, | |||
|         if vm_id is None: | ||||
|             raise Exception("Could not create VM") | ||||
| 
 | ||||
|         vm_pricing = VMPricing.get_vm_pricing_by_name( | ||||
|             name=specs['pricing_name'] | ||||
|         ) if 'pricing_name' in specs else VMPricing.get_default_pricing() | ||||
|         # Create a Hosting Order | ||||
|         order = HostingOrder.create( | ||||
|             price=final_price, | ||||
|             vm_id=vm_id, | ||||
|             customer=customer, | ||||
|             billing_address=billing_address | ||||
|             billing_address=billing_address, | ||||
|             vm_pricing=vm_pricing | ||||
|         ) | ||||
| 
 | ||||
|         # Create a Hosting Bill | ||||
|         HostingBill.create( | ||||
|             customer=customer, billing_address=billing_address) | ||||
|             customer=customer, billing_address=billing_address | ||||
|         ) | ||||
| 
 | ||||
|         # Create Billing Address for User if he does not have one | ||||
|         if not customer.user.billing_addresses.count(): | ||||
|  | @ -130,12 +138,16 @@ def create_vm_task(self, vm_template_id, user, specs, template, | |||
|             'cores': specs.get('cpu'), | ||||
|             'memory': specs.get('memory'), | ||||
|             'storage': specs.get('disk_size'), | ||||
|             'price': specs.get('price'), | ||||
|             'price': final_price, | ||||
|             'template': template.get('name'), | ||||
|             'vm_name': vm.get('name'), | ||||
|             'vm_id': vm['vm_id'], | ||||
|             'order_id': order.id | ||||
|         } | ||||
|         if 'pricing_name' in specs: | ||||
|             context['pricing'] = str(VMPricing.get_vm_pricing_by_name( | ||||
|                 name=specs['pricing_name'] | ||||
|             )) | ||||
|         email_data = { | ||||
|             'subject': settings.DCL_TEXT + " Order from %s" % context['email'], | ||||
|             'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, | ||||
|  |  | |||
|  | @ -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> | ||||
|                             <span>{% trans "Total" %}</span> | ||||
|                             <span class="pull-right">{{vm.price|intcomma}} CHF</span> | ||||
|                                 <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> | ||||
|                             <strong>{% trans "Total" %}</strong> | ||||
|                             <span class="pull-right">{{vm.total_price|floatformat:2|intcomma}} CHF</span> | ||||
|                         </p> | ||||
|                     </div> | ||||
|                 </div> | ||||
|  | @ -78,7 +88,7 @@ | |||
|             {% csrf_token %} | ||||
|             <div class="row"> | ||||
|                 <div class="col-sm-8"> | ||||
|                     <div class="dcl-place-order-text">{% blocktrans with vm_price=request.session.specs.price %}By clicking "Place order" this plan will charge your credit card account with the fee of {{ vm_price }}CHF/month{% endblocktrans %}.</div> | ||||
|                     <div class="dcl-place-order-text">{% blocktrans with vm_total_price=vm.total_price|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with the fee of {{vm_total_price}} CHF/month{% endblocktrans %}.</div> | ||||
|                 </div> | ||||
|                 <div class="col-sm-4 order-confirm-btn text-right"> | ||||
|                     <button class="btn choice-btn" id="btn-create-vm" data-toggle="modal" data-target="#createvm-modal"> | ||||
|  |  | |||
|  | @ -19,11 +19,11 @@ from hosting.models import HostingOrder | |||
| from membership.models import CustomUser, StripeCustomer | ||||
| from opennebula_api.serializers import VMTemplateSerializer | ||||
| from utils.forms import BillingAddressForm, BillingAddressFormSignup | ||||
| from utils.hosting_utils import get_vm_price | ||||
| from utils.hosting_utils import get_vm_price_with_vat | ||||
| from utils.stripe_utils import StripeUtils | ||||
| from utils.tasks import send_plain_email_task | ||||
| from .forms import ContactForm | ||||
| from .models import VMTemplate | ||||
| from .models import VMTemplate, VMPricing | ||||
| from .utils import get_cms_integration | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
|  | @ -93,7 +93,8 @@ class IndexView(CreateView): | |||
| 
 | ||||
|     @cache_control(no_cache=True, must_revalidate=True, no_store=True) | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         for session_var in ['specs', 'user', 'billing_address_data']: | ||||
|         for session_var in ['specs', 'user', 'billing_address_data', | ||||
|                             'pricing_name']: | ||||
|             if session_var in request.session: | ||||
|                 del request.session[session_var] | ||||
|         return HttpResponseRedirect(reverse('datacenterlight:cms_index')) | ||||
|  | @ -106,12 +107,30 @@ class IndexView(CreateView): | |||
|         storage = request.POST.get('storage') | ||||
|         storage_field = forms.IntegerField(validators=[self.validate_storage]) | ||||
|         template_id = int(request.POST.get('config')) | ||||
|         pricing_name = request.POST.get('pricing_name') | ||||
|         vm_pricing = VMPricing.get_vm_pricing_by_name(pricing_name) | ||||
| 
 | ||||
|         template = VMTemplate.objects.filter( | ||||
|             opennebula_vm_template_id=template_id | ||||
|         ).first() | ||||
|         template_data = VMTemplateSerializer(template).data | ||||
|         referer_url = request.META['HTTP_REFERER'] | ||||
| 
 | ||||
|         if vm_pricing is None: | ||||
|             vm_pricing_name_msg = _( | ||||
|                 "Incorrect pricing name. Please contact support" | ||||
|                 "{support_email}".format( | ||||
|                     support_email=settings.DCL_SUPPORT_FROM_ADDRESS | ||||
|                 ) | ||||
|             ) | ||||
|             messages.add_message( | ||||
|                 self.request, messages.ERROR, vm_pricing_name_msg, | ||||
|                 extra_tags='pricing' | ||||
|             ) | ||||
|             return HttpResponseRedirect(referer_url + "#order_form") | ||||
|         else: | ||||
|             vm_pricing_name = vm_pricing.name | ||||
| 
 | ||||
|         try: | ||||
|             cores = cores_field.clean(cores) | ||||
|         except ValidationError as err: | ||||
|  | @ -139,14 +158,21 @@ class IndexView(CreateView): | |||
|             ) | ||||
|             return HttpResponseRedirect(referer_url + "#order_form") | ||||
| 
 | ||||
|         amount_to_be_charged = get_vm_price( | ||||
|             cpu=cores, memory=memory, disk_size=storage | ||||
|         price, vat, vat_percent = get_vm_price_with_vat( | ||||
|             cpu=cores, | ||||
|             memory=memory, | ||||
|             ssd_size=storage, | ||||
|             pricing_name=vm_pricing_name | ||||
|         ) | ||||
|         specs = { | ||||
|             'cpu': cores, | ||||
|             'memory': memory, | ||||
|             'disk_size': storage, | ||||
|             'price': amount_to_be_charged | ||||
|             'price': price, | ||||
|             'vat': vat, | ||||
|             'vat_percent': vat_percent, | ||||
|             'total_price': price + vat, | ||||
|             'pricing_name': vm_pricing_name | ||||
|         } | ||||
|         request.session['specs'] = specs | ||||
|         request.session['template'] = template_data | ||||
|  | @ -220,7 +246,10 @@ class PaymentOrderView(FormView): | |||
|             'site_url': reverse('datacenterlight:index'), | ||||
|             'login_form': HostingUserLoginForm(prefix='login_form'), | ||||
|             'billing_address_form': billing_address_form, | ||||
|             'cms_integration': get_cms_integration('default') | ||||
|             'cms_integration': get_cms_integration('default'), | ||||
|             'vm_pricing': VMPricing.get_vm_pricing_by_name( | ||||
|                 self.request.session['specs']['pricing_name'] | ||||
|             ) | ||||
|         }) | ||||
|         return context | ||||
| 
 | ||||
|  | @ -393,7 +422,7 @@ class OrderConfirmationView(DetailView): | |||
|         cpu = specs.get('cpu') | ||||
|         memory = specs.get('memory') | ||||
|         disk_size = specs.get('disk_size') | ||||
|         amount_to_be_charged = specs.get('price') | ||||
|         amount_to_be_charged = specs.get('total_price') | ||||
|         plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu, | ||||
|                                                      memory=memory, | ||||
|                                                      disk_size=disk_size) | ||||
|  | @ -489,7 +518,7 @@ class OrderConfirmationView(DetailView): | |||
|                              stripe_subscription_obj.id, card_details_dict) | ||||
|         for session_var in ['specs', 'template', 'billing_address', | ||||
|                             'billing_address_data', | ||||
|                             'token', 'customer']: | ||||
|                             'token', 'customer', 'pricing_name']: | ||||
|             if session_var in request.session: | ||||
|                 del request.session[session_var] | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										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> | ||||
|                             <span>{% trans "Total" %}</span> | ||||
|                             <span class="pull-right">{{vm.price|intcomma}} CHF</span> | ||||
|                                 <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> | ||||
|                             <strong>{% trans "Total" %}</strong> | ||||
|                             <span class="pull-right">{% if vm.total_price %}{{vm.total_price|floatformat:2|intcomma}}{% else %}{{vm.price|floatformat:2|intcomma}}{% endif %} CHF</span> | ||||
|                         </p> | ||||
|                     </div> | ||||
|                 </div> | ||||
|  |  | |||
|  | @ -29,7 +29,7 @@ | |||
|                 <tr> | ||||
|                     <td class="xs-td-inline" data-header="{% trans 'Order Nr.' %}">{{ order.id }}</td> | ||||
|                     <td class="xs-td-bighalf" data-header="{% trans 'Date' %}">{{ order.created_at | date:"M d, Y H:i" }}</td> | ||||
|                     <td class="xs-td-smallhalf" data-header="{% trans 'Amount' %}">{{ order.price|intcomma }}</td> | ||||
|                     <td class="xs-td-smallhalf" data-header="{% trans 'Amount' %}">{{ order.price|floatformat:2|intcomma }}</td> | ||||
|                     <td class="text-right last-td"> | ||||
|                         <a class="btn btn-order-detail" href="{% url 'hosting:orders' order.pk %}">{% trans 'See Invoice' %}</a> | ||||
|                     </td> | ||||
|  |  | |||
|  | @ -45,7 +45,7 @@ | |||
| 				<h2 class="vm-detail-title">{% trans "Billing" %} <img src="{% static 'hosting/img/billing.svg' %}" class="un-icon"></h2> | ||||
| 				<div class="vm-vmid"> | ||||
| 					<div class="vm-item-subtitle">{% trans "Current Pricing" %}</div> | ||||
| 					<div class="vm-item-lg">{{virtual_machine.price|floatformat|intcomma}} CHF/{% trans "Month" %}</div> | ||||
| 					<div class="vm-item-lg">{{order.price|floatformat:2|intcomma}} CHF/{% trans "Month" %}</div> | ||||
| 					<a class="btn btn-vm-invoice" href="{% url 'hosting:orders' order.pk %}">{% trans "See Invoice" %}</a> | ||||
| 				</div> | ||||
| 			</div> | ||||
|  |  | |||
|  | @ -42,7 +42,7 @@ from utils.forms import ( | |||
|     BillingAddressForm, PasswordResetRequestForm, UserBillingAddressForm, | ||||
|     ResendActivationEmailForm | ||||
| ) | ||||
| from utils.hosting_utils import get_vm_price | ||||
| from utils.hosting_utils import get_vm_price, get_vm_price_with_vat | ||||
| from utils.mailer import BaseEmail | ||||
| from utils.stripe_utils import StripeUtils | ||||
| from utils.tasks import send_plain_email_task | ||||
|  | @ -749,11 +749,17 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): | |||
|                 context['vm'] = vm_detail.__dict__ | ||||
|                 context['vm']['name'] = '{}-{}'.format( | ||||
|                     context['vm']['configuration'], context['vm']['vm_id']) | ||||
|                 context['vm']['price'] = get_vm_price( | ||||
|                 price, vat, vat_percent = get_vm_price_with_vat( | ||||
|                     cpu=context['vm']['cores'], | ||||
|                     disk_size=context['vm']['disk_size'], | ||||
|                     memory=context['vm']['memory'] | ||||
|                     ssd_size=context['vm']['disk_size'], | ||||
|                     memory=context['vm']['memory'], | ||||
|                     pricing_name=(obj.vm_pricing.name | ||||
|                                   if obj.vm_pricing else 'default') | ||||
|                 ) | ||||
|                 context['vm']['vat'] = vat | ||||
|                 context['vm']['price'] = price | ||||
|                 context['vm']['vat_percent'] = vat_percent | ||||
|                 context['vm']['total_price'] = price + vat | ||||
|                 context['subscription_end_date'] = vm_detail.end_date() | ||||
|             except VMDetail.DoesNotExist: | ||||
|                 try: | ||||
|  | @ -762,6 +768,17 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): | |||
|                     ) | ||||
|                     vm = manager.get_vm(obj.vm_id) | ||||
|                     context['vm'] = VirtualMachineSerializer(vm).data | ||||
|                     price, vat, vat_percent = get_vm_price_with_vat( | ||||
|                         cpu=context['vm']['cores'], | ||||
|                         ssd_size=context['vm']['disk_size'], | ||||
|                         memory=context['vm']['memory'], | ||||
|                         pricing_name=(obj.vm_pricing.name | ||||
|                                       if obj.vm_pricing else 'default') | ||||
|                     ) | ||||
|                     context['vm']['vat'] = vat | ||||
|                     context['vm']['price'] = price | ||||
|                     context['vm']['vat_percent'] = vat_percent | ||||
|                     context['vm']['total_price'] = price + vat | ||||
|                 except WrongIdError: | ||||
|                     messages.error( | ||||
|                         self.request, | ||||
|  | @ -1100,7 +1117,8 @@ class VirtualMachineView(LoginRequiredMixin, View): | |||
|             context = { | ||||
|                 'virtual_machine': serializer.data, | ||||
|                 'order': HostingOrder.objects.get( | ||||
|                     vm_id=serializer.data['vm_id']) | ||||
|                     vm_id=serializer.data['vm_id'] | ||||
|                 ) | ||||
|             } | ||||
|         except Exception as ex: | ||||
|             logger.debug("Exception generated {}".format(str(ex))) | ||||
|  |  | |||
|  | @ -1,6 +1,8 @@ | |||
| import decimal | ||||
| import logging | ||||
| from oca.pool import WrongIdError | ||||
| 
 | ||||
| from datacenterlight.models import VMPricing | ||||
| from hosting.models import UserHostingKey, VMDetail | ||||
| from opennebula_api.serializers import VirtualMachineSerializer | ||||
| 
 | ||||
|  | @ -49,14 +51,74 @@ def get_or_create_vm_detail(user, manager, vm_id): | |||
|     return vm_detail_obj | ||||
| 
 | ||||
| 
 | ||||
| def get_vm_price(cpu, memory, disk_size): | ||||
| def get_vm_price(cpu, memory, disk_size, hdd_size=0, pricing_name='default'): | ||||
|     """ | ||||
|     A helper function that computes price of a VM from given cpu, ram and | ||||
|     ssd parameters | ||||
| 
 | ||||
|     :param cpu: Number of cores of the VM | ||||
|     :param memory: RAM of the VM | ||||
|     :param disk_size: Disk space of the VM | ||||
|     :param disk_size: Disk space of the VM (SSD) | ||||
|     :param hdd_size: The HDD size | ||||
|     :param pricing_name: The pricing name to be used | ||||
|     :return: The price of the VM | ||||
|     """ | ||||
|     return (cpu * 5) + (memory * 2) + (disk_size * 0.6) | ||||
|     try: | ||||
|         pricing = VMPricing.objects.get(name=pricing_name) | ||||
|     except Exception as ex: | ||||
|         logger.error( | ||||
|             "Error getting VMPricing object for {pricing_name}." | ||||
|             "Details: {details}".format( | ||||
|                 pricing_name=pricing_name, details=str(ex) | ||||
|             ) | ||||
|         ) | ||||
|         return None | ||||
|     price = ((decimal.Decimal(cpu) * pricing.cores_unit_price) + | ||||
|              (decimal.Decimal(memory) * pricing.ram_unit_price) + | ||||
|              (decimal.Decimal(disk_size) * pricing.ssd_unit_price) + | ||||
|              (decimal.Decimal(hdd_size) * pricing.hdd_unit_price)) | ||||
|     cents = decimal.Decimal('.01') | ||||
|     price = price.quantize(cents, decimal.ROUND_HALF_UP) | ||||
|     return float(price) | ||||
| 
 | ||||
| 
 | ||||
| def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0, | ||||
|                           pricing_name='default'): | ||||
|     """ | ||||
|     A helper function that computes price of a VM from given cpu, ram and | ||||
|     ssd, hdd and the pricing parameters | ||||
| 
 | ||||
|     :param cpu: Number of cores of the VM | ||||
|     :param memory: RAM of the VM | ||||
|     :param ssd_size: Disk space of the VM (SSD) | ||||
|     :param hdd_size: The HDD size | ||||
|     :param pricing_name: The pricing name to be used | ||||
|     :return: The a tuple containing the price of the VM, the VAT and the | ||||
|              VAT percentage | ||||
|     """ | ||||
|     try: | ||||
|         pricing = VMPricing.objects.get(name=pricing_name) | ||||
|     except Exception as ex: | ||||
|         logger.error( | ||||
|             "Error getting VMPricing object for {pricing_name}." | ||||
|             "Details: {details}".format( | ||||
|                 pricing_name=pricing_name, details=str(ex) | ||||
|             ) | ||||
|         ) | ||||
|         return None | ||||
| 
 | ||||
|     price = ((decimal.Decimal(cpu) * pricing.cores_unit_price) + | ||||
|              (decimal.Decimal(memory) * pricing.ram_unit_price) + | ||||
|              (decimal.Decimal(ssd_size) * pricing.ssd_unit_price) + | ||||
|              (decimal.Decimal(hdd_size) * pricing.hdd_unit_price)) | ||||
|     if pricing.vat_inclusive: | ||||
|         vat = decimal.Decimal(0) | ||||
|         vat_percent = decimal.Decimal(0) | ||||
|     else: | ||||
|         vat = price * pricing.vat_percentage * decimal.Decimal(0.01) | ||||
|         vat_percent = pricing.vat_percentage | ||||
| 
 | ||||
|     cents = decimal.Decimal('.01') | ||||
|     price = price.quantize(cents, decimal.ROUND_HALF_UP) | ||||
|     vat = vat.quantize(cents, decimal.ROUND_HALF_UP) | ||||
|     return float(price), float(vat), float(vat_percent) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue