From df368c715f575d28876758d9659ea3f334680da4 Mon Sep 17 00:00:00 2001 From: sanghee Date: Tue, 13 Nov 2018 21:21:46 +0100 Subject: [PATCH 001/795] changed topnav font into 400 by default --- datacenterlight/static/datacenterlight/css/landing-page.css | 1 + 1 file changed, 1 insertion(+) diff --git a/datacenterlight/static/datacenterlight/css/landing-page.css b/datacenterlight/static/datacenterlight/css/landing-page.css index f241ed71..cf14474f 100755 --- a/datacenterlight/static/datacenterlight/css/landing-page.css +++ b/datacenterlight/static/datacenterlight/css/landing-page.css @@ -73,6 +73,7 @@ textarea { .navbar { transition: all .3s ease-in; + font-weight: 400; } .navbar-default .navbar-nav>.open>a, From f730f4c00df35ea7de91ae8d952acbaf5c6d0709 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 31 Jan 2019 21:14:31 +0100 Subject: [PATCH 002/795] Update Changelog --- Changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog b/Changelog index 23a6e52a..a9b537d9 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,5 @@ +Next: + * #5151: [all] Add GDPR banner (MR!678 by pcoder) 2.4.7: 2019-01-30 * #6213: [cms] Make ungleich product items equal heights and add option to animate or not animate the contents (MR!682 by Aatish) 2.4.6: 2019-01-09 From 541b08584c4688b343953e55627e32492288313b Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 31 Jan 2019 21:24:36 +0100 Subject: [PATCH 003/795] Update Changelog for 2.4.8 --- Changelog | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Changelog b/Changelog index a9b537d9..301e936c 100644 --- a/Changelog +++ b/Changelog @@ -1,5 +1,6 @@ -Next: +2.4.8: 2019-01-31 * #5151: [all] Add GDPR banner (MR!678 by pcoder) + * #6059: [dcl] Change topnav font to 400 by default (MR! 680 by Sanghee) 2.4.7: 2019-01-30 * #6213: [cms] Make ungleich product items equal heights and add option to animate or not animate the contents (MR!682 by Aatish) 2.4.6: 2019-01-09 From 2ae3e695e873077476c390724ee661e022e5876d Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 31 Jan 2019 22:43:56 +0100 Subject: [PATCH 004/795] Add styles --- templates/gdpr/gdpr_banner.html | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/templates/gdpr/gdpr_banner.html b/templates/gdpr/gdpr_banner.html index 6368982e..5974a924 100644 --- a/templates/gdpr/gdpr_banner.html +++ b/templates/gdpr/gdpr_banner.html @@ -2,6 +2,15 @@ {% if request.COOKIES.gdpr_accepted or IS_TENANT_SITE %} {% else %} -
+
{% blocktrans %}This site uses cookies for analytics. By continuing to browse this site, you agree to use this.{% endblocktrans %}  {% trans "Learn more" %}
- diff --git a/digitalglarus/templates/digitalglarus/membership_pricing.html b/digitalglarus/templates/digitalglarus/membership_pricing.html index 1342a365..28de2bda 100644 --- a/digitalglarus/templates/digitalglarus/membership_pricing.html +++ b/digitalglarus/templates/digitalglarus/membership_pricing.html @@ -123,7 +123,7 @@ In der Au 7 Schwanden 8762 Switzerland
info@digitalglarus.ch
- (044) 534-66-22 + +41 55 505 6266

 

diff --git a/digitalglarus/templates/digitalglarus/reset_password.html b/digitalglarus/templates/digitalglarus/reset_password.html index 9272ce36..2cd0c2b9 100644 --- a/digitalglarus/templates/digitalglarus/reset_password.html +++ b/digitalglarus/templates/digitalglarus/reset_password.html @@ -46,7 +46,7 @@ In der Au 7 Schwanden 8762 Switzerland
info@digitalglarus.ch
- (044) 534-66-22 + +41 55 505 6266

 

diff --git a/digitalglarus/templates/digitalglarus/signup.html b/digitalglarus/templates/digitalglarus/signup.html index 6a46294a..fbc8646c 100644 --- a/digitalglarus/templates/digitalglarus/signup.html +++ b/digitalglarus/templates/digitalglarus/signup.html @@ -50,7 +50,7 @@ In der Au 7 Schwanden 8762 Switzerland
info@digitalglarus.ch
- (044) 534-66-22 + +41 55 505 6266

 

diff --git a/digitalglarus/templates/digitalglarus/user_billing_address.html b/digitalglarus/templates/digitalglarus/user_billing_address.html index ec5fddf2..b5e86d56 100644 --- a/digitalglarus/templates/digitalglarus/user_billing_address.html +++ b/digitalglarus/templates/digitalglarus/user_billing_address.html @@ -78,7 +78,7 @@ In der Au 7 Schwanden 8762 Switzerland
info@digitalglarus.ch
- (044) 534-66-22 + +41 55 505 6266

 

diff --git a/templates/gdpr/gdpr_banner.html b/templates/gdpr/gdpr_banner.html index 76e60008..7e9f5c7f 100644 --- a/templates/gdpr/gdpr_banner.html +++ b/templates/gdpr/gdpr_banner.html @@ -120,7 +120,7 @@ ungleich glarus ag
Bahnhofstrasse 1
8783 Linthal (CH)
- Tel.: (044) 534-66-22
+ Tel.: +41 55 505 6266
E-Mail: info@ungleich.ch

This privacy statement applies to the web content of diff --git a/ungleich_page/templates/ungleich_page/includes/_contact_us.html b/ungleich_page/templates/ungleich_page/includes/_contact_us.html index 1038f358..55b86706 100644 --- a/ungleich_page/templates/ungleich_page/includes/_contact_us.html +++ b/ungleich_page/templates/ungleich_page/includes/_contact_us.html @@ -22,7 +22,7 @@ info@ungleich.ch -

(044) 534-66-22

+

+41 55 505 6266

From 46c3bb1ce4dd6fa2b57c166a8c3a2cf57c7dc810 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 14 Feb 2019 20:56:12 +0100 Subject: [PATCH 020/795] Update Changelog for 2.4.10 --- Changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog b/Changelog index 0c6c61d1..96108b80 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,5 @@ +2.4.10: 2019-02-14 + * #6451: [dg, gdpr banner] Update phone number (MR!688 by pcoder) 2.4.9: 2019-02-01 * bugfix: Fix inconsistent styles in GDPR modal (MR!679 by pcoder) 2.4.8: 2019-01-31 From 0e84081880404955b5c2746e731f9ee026ba78e9 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 2 Apr 2019 09:18:15 +0200 Subject: [PATCH 021/795] Add monthlyhostingbill model + code --- hosting/models.py | 22 ++++++++++++++++++++++ utils/stripe_utils.py | 20 ++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/hosting/models.py b/hosting/models.py index 707b072d..4e4e2a59 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -232,6 +232,28 @@ class HostingBill(AssignPermissionsMixin, models.Model): return instance +class MonthlyHostingBill(AssignPermissionsMixin, models.Model): + customer = models.ForeignKey(StripeCustomer) + order = models.ForeignKey(HostingOrder) + receipt_number = models.CharField( + help_text="The receipt number that is generated on Stripe" + ) + invoice_number = models.CharField( + help_text="The invoice number that is generated on Stripe" + ) + billing_period = models.CharField( + help_text="The billing period for which the bill is valid" + ) + date_paid = models.DateField(help_text="Date on which the bill was paid") + + permissions = ('view_monthlyhostingbill',) + + class Meta: + permissions = ( + ('view_monthlyhostingbill', 'View Monthly Hosting'), + ) + + class VMDetail(models.Model): user = models.ForeignKey(CustomUser) vm_id = models.IntegerField(default=0) diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py index a3224a0e..d412cbd0 100644 --- a/utils/stripe_utils.py +++ b/utils/stripe_utils.py @@ -122,6 +122,26 @@ class StripeUtils(object): } return card_details + @handleStripeError + def get_all_invoices(self, customer_id): + invoices = stripe.Invoice.list(limit=100, customer=customer_id) + return_list = [] + for invoice in invoices: + invoice_details = { + 'created': invoice.created, + 'receipt_number': invoice.receipt_number, + 'invoice_number': invoice.number, + 'date_paid': invoice.date_paid, + 'period_start': invoice.period_start, + 'period_end': invoice.period_end, + 'paid': invoice.paid, + 'billing_reason': invoice.billing_reason, + 'discount': invoice.discount.coupon.amount_off if invoice.discount else 0, + 'total': invoice.total + } + return_list.append(invoice_details) + return return_list + @handleStripeError def get_cards_details_from_token(self, token): stripe_token = stripe.Token.retrieve(token) From 8dc00c9dd9040d1921bee56c3a67fd908def5ee3 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 2 Apr 2019 09:18:46 +0200 Subject: [PATCH 022/795] Add management command --- .../management/commands/fetch_stripe_bills.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 hosting/management/commands/fetch_stripe_bills.py diff --git a/hosting/management/commands/fetch_stripe_bills.py b/hosting/management/commands/fetch_stripe_bills.py new file mode 100644 index 00000000..3a06a1d9 --- /dev/null +++ b/hosting/management/commands/fetch_stripe_bills.py @@ -0,0 +1,28 @@ +from django.core.management.base import BaseCommand + +from hosting.models import UserCardDetail +from membership.models import CustomUser +from utils.stripe_utils import StripeUtils + + +class Command(BaseCommand): + help = '''Fetches invoices from Stripe and creates bills for a given + customer in the MonthlyHostingBill model''' + + def add_arguments(self, parser): + parser.add_argument('customer_email', nargs='+', type=str) + + def handle(self, *args, **options): + try: + for email in options['customer_email']: + stripe_utils = StripeUtils() + user = CustomUser.objects.get(email=email) + if hasattr(user, 'stripecustomer'): + self.stdout.write(self.style.SUCCESS('Found %s. Fetching bills for him.' % email)) + stripe_utils.get_all_invoices( + user.stripecustomer.stripe_id + ) + else: + self.stdout.write(self.style.SUCCESS('Customer email %s does not have a stripe customer.' % email)) + except Exception as e: + print(" *** Error occurred. Details {}".format(str(e))) From 6d42f88be1c2d3955812ac50a3ada803afcabe35 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 06:12:48 +0200 Subject: [PATCH 023/795] Complete implementation of fetch_stripe_bills --- .../management/commands/fetch_stripe_bills.py | 35 +++++++++++-- hosting/models.py | 38 +++++++++++--- utils/stripe_utils.py | 51 +++++++++++++------ 3 files changed, 97 insertions(+), 27 deletions(-) diff --git a/hosting/management/commands/fetch_stripe_bills.py b/hosting/management/commands/fetch_stripe_bills.py index 3a06a1d9..6285ec0a 100644 --- a/hosting/management/commands/fetch_stripe_bills.py +++ b/hosting/management/commands/fetch_stripe_bills.py @@ -1,9 +1,13 @@ +import logging + from django.core.management.base import BaseCommand -from hosting.models import UserCardDetail +from hosting.models import MonthlyHostingBill from membership.models import CustomUser from utils.stripe_utils import StripeUtils +logger = logging.getLogger(__name__) + class Command(BaseCommand): help = '''Fetches invoices from Stripe and creates bills for a given @@ -18,11 +22,32 @@ class Command(BaseCommand): stripe_utils = StripeUtils() user = CustomUser.objects.get(email=email) if hasattr(user, 'stripecustomer'): - self.stdout.write(self.style.SUCCESS('Found %s. Fetching bills for him.' % email)) - stripe_utils.get_all_invoices( - user.stripecustomer.stripe_id + self.stdout.write(self.style.SUCCESS( + 'Found %s. Fetching bills for him.' % email)) + mhb = MonthlyHostingBill.objects.last( + customer=user.stripecustomer ) + created_gt = {} + if mhb is not None: + # fetch only invoices which is created after + # mhb.created, because we already have invoices till + # this date + created_gt = {'gt': mhb.created} + + all_invoices_response = stripe_utils.get_all_invoices( + user.stripecustomer.stripe_id, + created=created_gt + ) + all_invoices = all_invoices_response['response_object'] + logger.debug( + "Obtained {} invoices".format(len(all_invoices)) + ) + for invoice in all_invoices: + MonthlyHostingBill.create( + invoice, stripe_customer=user.stripecustomer + ) else: - self.stdout.write(self.style.SUCCESS('Customer email %s does not have a stripe customer.' % email)) + self.stdout.write(self.style.SUCCESS( + 'Customer email %s does not have a stripe customer.' % email)) except Exception as e: print(" *** Error occurred. Details {}".format(str(e))) diff --git a/hosting/models.py b/hosting/models.py index 4e4e2a59..856e83ea 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -235,16 +235,24 @@ class HostingBill(AssignPermissionsMixin, models.Model): class MonthlyHostingBill(AssignPermissionsMixin, models.Model): customer = models.ForeignKey(StripeCustomer) order = models.ForeignKey(HostingOrder) + created = models.DateTimeField(help_text="When the invoice was created") receipt_number = models.CharField( - help_text="The receipt number that is generated on Stripe" + help_text="The receipt number that is generated on Stripe", + max_length=100 ) invoice_number = models.CharField( - help_text="The invoice number that is generated on Stripe" + help_text="The invoice number that is generated on Stripe", + max_length=100 ) - billing_period = models.CharField( - help_text="The billing period for which the bill is valid" - ) - date_paid = models.DateField(help_text="Date on which the bill was paid") + paid_at = models.DateTimeField(help_text="Date on which the bill was paid") + period_start = models.DateTimeField() + period_end = models.DateTimeField() + billing_reason = models.CharField(max_length=25) + discount = models.PositiveIntegerField() + total = models.IntegerField() + lines_data_count = models.IntegerField() + invoice_id = models.CharField(unique=True, max_length=100) + lines_meta_data_csv = models.TextField() permissions = ('view_monthlyhostingbill',) @@ -253,6 +261,24 @@ class MonthlyHostingBill(AssignPermissionsMixin, models.Model): ('view_monthlyhostingbill', 'View Monthly Hosting'), ) + @classmethod + def create(cls, stripe_customer, **args): + instance = cls.objects.create(args) + instance.customer = stripe_customer + if len(instance.lines_meta_data_csv) > 0: + vm_ids = [vm_id.strip() for vm_id in instance.lines_meta_data_csv.split(",")] + if len(vm_ids) == 1: + instance.order = HostingOrder.objects.get(vm_id=vm_ids[0]) + else: + logger.debug( + "More than one VM_ID" + "for MonthlyHostingBill {}".format(instance.invoice_id) + ) + logger.debug("VM_IDS=".format(','.join(vm_ids))) + instance.assign_permissions(stripe_customer.user) + instance.save() + return instance + class VMDetail(models.Model): user = models.ForeignKey(CustomUser) diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py index d412cbd0..63df0133 100644 --- a/utils/stripe_utils.py +++ b/utils/stripe_utils.py @@ -123,23 +123,42 @@ class StripeUtils(object): return card_details @handleStripeError - def get_all_invoices(self, customer_id): - invoices = stripe.Invoice.list(limit=100, customer=customer_id) + def get_all_invoices(self, customer_id, created): return_list = [] - for invoice in invoices: - invoice_details = { - 'created': invoice.created, - 'receipt_number': invoice.receipt_number, - 'invoice_number': invoice.number, - 'date_paid': invoice.date_paid, - 'period_start': invoice.period_start, - 'period_end': invoice.period_end, - 'paid': invoice.paid, - 'billing_reason': invoice.billing_reason, - 'discount': invoice.discount.coupon.amount_off if invoice.discount else 0, - 'total': invoice.total - } - return_list.append(invoice_details) + has_more_invoices = True + starting_after = False + while has_more_invoices: + if starting_after: + invoices = stripe.Invoice.list( + limit=10, customer=customer_id, created=created + ) + else: + invoices = stripe.Invoice.list( + limit=10, customer=customer_id, created=created, + starting_after=starting_after + ) + has_more_invoices = invoices.has_more + for invoice in invoices.data: + invoice_details = { + 'created': invoice.created, + 'receipt_number': invoice.receipt_number, + 'invoice_number': invoice.number, + 'paid_at': invoice.status_transitions.paid_at if invoice.paid else 0, + 'period_start': invoice.period_start, + 'period_end': invoice.period_end, + 'billing_reason': invoice.billing_reason, + 'discount': invoice.discount.coupon.amount_off if invoice.discount else 0, + 'total': invoice.total, + # to see how many line items we have in this invoice and + # then later check if we have more than 1 + 'lines_data_count': len(invoice.lines.data), + 'invoice_id': invoice.id, + 'lines_meta_data_csv': ','.join( + [line.metadata.VM_ID for line in invoice.lines.data] + ) + } + starting_after = invoice.id + return_list.append(invoice_details) return return_list @handleStripeError From 0bc8c350312216baf3a23ea3d2d1a366eefe6ae8 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 06:13:12 +0200 Subject: [PATCH 024/795] Add migration --- hosting/migrations/0050_monthlyhostingbill.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 hosting/migrations/0050_monthlyhostingbill.py diff --git a/hosting/migrations/0050_monthlyhostingbill.py b/hosting/migrations/0050_monthlyhostingbill.py new file mode 100644 index 00000000..34d29e68 --- /dev/null +++ b/hosting/migrations/0050_monthlyhostingbill.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2019-04-03 03:47 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import utils.mixins + + +class Migration(migrations.Migration): + + dependencies = [ + ('membership', '0007_auto_20180213_0128'), + ('hosting', '0049_auto_20181005_0736'), + ] + + operations = [ + migrations.CreateModel( + name='MonthlyHostingBill', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(help_text='When the invoice was created')), + ('receipt_number', models.CharField(help_text='The receipt number that is generated on Stripe', max_length=100)), + ('invoice_number', models.CharField(help_text='The invoice number that is generated on Stripe', max_length=100)), + ('paid_at', models.DateTimeField(help_text='Date on which the bill was paid')), + ('period_start', models.DateTimeField()), + ('period_end', models.DateTimeField()), + ('billing_reason', models.CharField(max_length=25)), + ('discount', models.PositiveIntegerField()), + ('total', models.IntegerField()), + ('lines_data_count', models.IntegerField()), + ('invoice_id', models.CharField(max_length=100, unique=True)), + ('lines_meta_data_csv', models.TextField()), + ('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='membership.StripeCustomer')), + ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hosting.HostingOrder')), + ], + options={ + 'permissions': (('view_monthlyhostingbill', 'View Monthly Hosting'),), + }, + bases=(utils.mixins.AssignPermissionsMixin, models.Model), + ), + ] From b1566c4c61605ff5f9122af6a00d10ebfb2b2dfd Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 06:22:49 +0200 Subject: [PATCH 025/795] Get the last monthly hosting bill --- hosting/management/commands/fetch_stripe_bills.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hosting/management/commands/fetch_stripe_bills.py b/hosting/management/commands/fetch_stripe_bills.py index 6285ec0a..fb4718c7 100644 --- a/hosting/management/commands/fetch_stripe_bills.py +++ b/hosting/management/commands/fetch_stripe_bills.py @@ -24,9 +24,8 @@ class Command(BaseCommand): if hasattr(user, 'stripecustomer'): self.stdout.write(self.style.SUCCESS( 'Found %s. Fetching bills for him.' % email)) - mhb = MonthlyHostingBill.objects.last( - customer=user.stripecustomer - ) + mhb = MonthlyHostingBill.objects.filter( + customer=user.stripecustomer).last() created_gt = {} if mhb is not None: # fetch only invoices which is created after From 6f1449836a654cad5eefe23720d07764017de077 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 06:27:54 +0200 Subject: [PATCH 026/795] Fix a bug: use starting_after if its defined --- utils/stripe_utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py index 63df0133..ee6e1b18 100644 --- a/utils/stripe_utils.py +++ b/utils/stripe_utils.py @@ -130,12 +130,12 @@ class StripeUtils(object): while has_more_invoices: if starting_after: invoices = stripe.Invoice.list( - limit=10, customer=customer_id, created=created + limit=10, customer=customer_id, created=created, + starting_after=starting_after ) else: invoices = stripe.Invoice.list( - limit=10, customer=customer_id, created=created, - starting_after=starting_after + limit=10, customer=customer_id, created=created ) has_more_invoices = invoices.has_more for invoice in invoices.data: From b7dd4acb0798d655a9becc8c5e09d0a37071f4f4 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 06:33:28 +0200 Subject: [PATCH 027/795] Correct the way of getting VM_ID meta data --- utils/stripe_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py index ee6e1b18..53346626 100644 --- a/utils/stripe_utils.py +++ b/utils/stripe_utils.py @@ -154,7 +154,7 @@ class StripeUtils(object): 'lines_data_count': len(invoice.lines.data), 'invoice_id': invoice.id, 'lines_meta_data_csv': ','.join( - [line.metadata.VM_ID for line in invoice.lines.data] + [line.metadata.VM_ID if line.metadata.VM_ID is not None else '' for line in invoice.lines.data] ) } starting_after = invoice.id From 3eaa53ca78bb7e52027853ce53409d46493fd471 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 06:36:28 +0200 Subject: [PATCH 028/795] Use stdout instead of logger --- hosting/management/commands/fetch_stripe_bills.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hosting/management/commands/fetch_stripe_bills.py b/hosting/management/commands/fetch_stripe_bills.py index fb4718c7..89fffb27 100644 --- a/hosting/management/commands/fetch_stripe_bills.py +++ b/hosting/management/commands/fetch_stripe_bills.py @@ -38,9 +38,7 @@ class Command(BaseCommand): created=created_gt ) all_invoices = all_invoices_response['response_object'] - logger.debug( - "Obtained {} invoices".format(len(all_invoices)) - ) + self.stdout.write(self.style.SUCCESS("Obtained {} invoices".format(len(all_invoices)))) for invoice in all_invoices: MonthlyHostingBill.create( invoice, stripe_customer=user.stripecustomer From 033db01810643f9c9129b57103269ee7622b70cf Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 06:51:19 +0200 Subject: [PATCH 029/795] Correct error in getting lines data count --- utils/stripe_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py index 53346626..834b2201 100644 --- a/utils/stripe_utils.py +++ b/utils/stripe_utils.py @@ -151,7 +151,7 @@ class StripeUtils(object): 'total': invoice.total, # to see how many line items we have in this invoice and # then later check if we have more than 1 - 'lines_data_count': len(invoice.lines.data), + 'lines_data_count': len(invoice.lines.data) if invoice.lines.data is not None else 0, 'invoice_id': invoice.id, 'lines_meta_data_csv': ','.join( [line.metadata.VM_ID if line.metadata.VM_ID is not None else '' for line in invoice.lines.data] From c85a4f379652c8fb19031164b234c84f442db885 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 06:59:05 +0200 Subject: [PATCH 030/795] Catch error from stripe call --- hosting/management/commands/fetch_stripe_bills.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hosting/management/commands/fetch_stripe_bills.py b/hosting/management/commands/fetch_stripe_bills.py index 89fffb27..7219341b 100644 --- a/hosting/management/commands/fetch_stripe_bills.py +++ b/hosting/management/commands/fetch_stripe_bills.py @@ -37,8 +37,11 @@ class Command(BaseCommand): user.stripecustomer.stripe_id, created=created_gt ) + if all_invoices_response['error'] is not None: + self.stdout.write(self.style.ERROR(all_invoices_response['error'])) + exit(1) all_invoices = all_invoices_response['response_object'] - self.stdout.write(self.style.SUCCESS("Obtained {} invoices".format(len(all_invoices)))) + self.stdout.write(self.style.SUCCESS("Obtained {} invoices".format(len(all_invoices) if all_invoices is not None else 0))) for invoice in all_invoices: MonthlyHostingBill.create( invoice, stripe_customer=user.stripecustomer From dbf3b92c063b5b965e50874522cbd8927e32cf58 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 07:08:39 +0200 Subject: [PATCH 031/795] Add logging and verbosity --- .../management/commands/fetch_stripe_bills.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/hosting/management/commands/fetch_stripe_bills.py b/hosting/management/commands/fetch_stripe_bills.py index 7219341b..cbb09a2e 100644 --- a/hosting/management/commands/fetch_stripe_bills.py +++ b/hosting/management/commands/fetch_stripe_bills.py @@ -1,4 +1,5 @@ import logging +import sys from django.core.management.base import BaseCommand @@ -13,10 +14,32 @@ class Command(BaseCommand): help = '''Fetches invoices from Stripe and creates bills for a given customer in the MonthlyHostingBill model''' + def set_logger(self, verbosity): + """ + Set logger level based on verbosity option + """ + handler = logging.StreamHandler(sys.stdout) + formatter = logging.Formatter('%(asctime)s|%(levelname)s|%(module)s| %(message)s') + handler.setFormatter(formatter) + logger.addHandler(handler) + + if verbosity == 0: + self.logger.setLevel(logging.WARN) + elif verbosity == 1: # default + self.logger.setLevel(logging.INFO) + elif verbosity > 1: + self.logger.setLevel(logging.DEBUG) + + # verbosity 3: also enable all logging statements that reach the root + # logger + if verbosity > 2: + logging.getLogger().setLevel(logging.DEBUG) + def add_arguments(self, parser): parser.add_argument('customer_email', nargs='+', type=str) def handle(self, *args, **options): + self.set_logger(options.get('verbosity')) try: for email in options['customer_email']: stripe_utils = StripeUtils() From 66ffbf38aa729ba753878bcccb6b799fb4f58582 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 07:32:18 +0200 Subject: [PATCH 032/795] Handle if VM_ID metadata is not set --- utils/stripe_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py index 834b2201..7211465a 100644 --- a/utils/stripe_utils.py +++ b/utils/stripe_utils.py @@ -154,7 +154,7 @@ class StripeUtils(object): 'lines_data_count': len(invoice.lines.data) if invoice.lines.data is not None else 0, 'invoice_id': invoice.id, 'lines_meta_data_csv': ','.join( - [line.metadata.VM_ID if line.metadata.VM_ID is not None else '' for line in invoice.lines.data] + [line.metadata.VM_ID if hasattr(line.metadata, 'VM_ID') else '' for line in invoice.lines.data] ) } starting_after = invoice.id From 444f79eab7f03724be7ec5bfef165229895475e0 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 07:35:43 +0200 Subject: [PATCH 033/795] Remove unwanted logger code --- .../management/commands/fetch_stripe_bills.py | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/hosting/management/commands/fetch_stripe_bills.py b/hosting/management/commands/fetch_stripe_bills.py index cbb09a2e..7219341b 100644 --- a/hosting/management/commands/fetch_stripe_bills.py +++ b/hosting/management/commands/fetch_stripe_bills.py @@ -1,5 +1,4 @@ import logging -import sys from django.core.management.base import BaseCommand @@ -14,32 +13,10 @@ class Command(BaseCommand): help = '''Fetches invoices from Stripe and creates bills for a given customer in the MonthlyHostingBill model''' - def set_logger(self, verbosity): - """ - Set logger level based on verbosity option - """ - handler = logging.StreamHandler(sys.stdout) - formatter = logging.Formatter('%(asctime)s|%(levelname)s|%(module)s| %(message)s') - handler.setFormatter(formatter) - logger.addHandler(handler) - - if verbosity == 0: - self.logger.setLevel(logging.WARN) - elif verbosity == 1: # default - self.logger.setLevel(logging.INFO) - elif verbosity > 1: - self.logger.setLevel(logging.DEBUG) - - # verbosity 3: also enable all logging statements that reach the root - # logger - if verbosity > 2: - logging.getLogger().setLevel(logging.DEBUG) - def add_arguments(self, parser): parser.add_argument('customer_email', nargs='+', type=str) def handle(self, *args, **options): - self.set_logger(options.get('verbosity')) try: for email in options['customer_email']: stripe_utils = StripeUtils() From 12b8a778623de9121a2246ac0506264070aabe33 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 09:03:58 +0200 Subject: [PATCH 034/795] Fix issues and also include subscription_id --- .../management/commands/fetch_stripe_bills.py | 5 +- hosting/migrations/0051_auto_20190403_0703.py | 25 +++++++ hosting/models.py | 65 ++++++++++++++++--- utils/stripe_utils.py | 5 +- 4 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 hosting/migrations/0051_auto_20190403_0703.py diff --git a/hosting/management/commands/fetch_stripe_bills.py b/hosting/management/commands/fetch_stripe_bills.py index 7219341b..8f35aa8c 100644 --- a/hosting/management/commands/fetch_stripe_bills.py +++ b/hosting/management/commands/fetch_stripe_bills.py @@ -43,9 +43,8 @@ class Command(BaseCommand): all_invoices = all_invoices_response['response_object'] self.stdout.write(self.style.SUCCESS("Obtained {} invoices".format(len(all_invoices) if all_invoices is not None else 0))) for invoice in all_invoices: - MonthlyHostingBill.create( - invoice, stripe_customer=user.stripecustomer - ) + invoice['customer'] = user.stripecustomer + MonthlyHostingBill.create(invoice) else: self.stdout.write(self.style.SUCCESS( 'Customer email %s does not have a stripe customer.' % email)) diff --git a/hosting/migrations/0051_auto_20190403_0703.py b/hosting/migrations/0051_auto_20190403_0703.py new file mode 100644 index 00000000..d1f87e53 --- /dev/null +++ b/hosting/migrations/0051_auto_20190403_0703.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2019-04-03 07:03 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('hosting', '0050_monthlyhostingbill'), + ] + + operations = [ + migrations.AddField( + model_name='monthlyhostingbill', + name='subscription_ids_csv', + field=models.TextField(default=''), + ), + migrations.AlterField( + model_name='monthlyhostingbill', + name='lines_meta_data_csv', + field=models.TextField(default=''), + ), + ] diff --git a/hosting/models.py b/hosting/models.py index 856e83ea..c976c336 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -1,8 +1,10 @@ import logging import os +import pytz from Crypto.PublicKey import RSA from dateutil.relativedelta import relativedelta +from datetime import datetime from django.db import models from django.utils import timezone from django.utils.functional import cached_property @@ -252,7 +254,8 @@ class MonthlyHostingBill(AssignPermissionsMixin, models.Model): total = models.IntegerField() lines_data_count = models.IntegerField() invoice_id = models.CharField(unique=True, max_length=100) - lines_meta_data_csv = models.TextField() + lines_meta_data_csv = models.TextField(default="") + subscription_ids_csv = models.TextField(default="") permissions = ('view_monthlyhostingbill',) @@ -262,21 +265,63 @@ class MonthlyHostingBill(AssignPermissionsMixin, models.Model): ) @classmethod - def create(cls, stripe_customer, **args): - instance = cls.objects.create(args) - instance.customer = stripe_customer - if len(instance.lines_meta_data_csv) > 0: - vm_ids = [vm_id.strip() for vm_id in instance.lines_meta_data_csv.split(",")] + def create(cls, args): + # Try to infer the HostingOrder from subscription id or VM_ID + if len(args['subscription_ids_csv']) > 0: + sub_ids = [sub_id.strip() for sub_id in args['subscription_ids_csv'].split(",")] + if len(sub_ids) == 1: + args['order'] = HostingOrder.objects.get( + subscription_id=sub_ids[0] + ) + else: + logger.debug( + "More than one subscriptions" + "for MonthlyHostingBill {}".format(args['invoice_id']) + ) + logger.debug("SUB_IDS=".format(','.join(sub_ids))) + logger.debug("Not importing invoices") + return + elif len(args['lines_meta_data_csv']) > 0: + vm_ids = [vm_id.strip() for vm_id in args['lines_meta_data_csv'].split(",")] if len(vm_ids) == 1: - instance.order = HostingOrder.objects.get(vm_id=vm_ids[0]) + args['order'] = HostingOrder.objects.get(vm_id=vm_ids[0]) else: logger.debug( "More than one VM_ID" - "for MonthlyHostingBill {}".format(instance.invoice_id) + "for MonthlyHostingBill {}".format(args['invoice_id']) ) logger.debug("VM_IDS=".format(','.join(vm_ids))) - instance.assign_permissions(stripe_customer.user) - instance.save() + logger.debug("Not importing invoices") + return + else: + logger.debug("Neither subscription id nor vm_id available") + logger.debug("Can't import invoice") + return + + instance = cls.objects.create( + created=datetime.utcfromtimestamp( + args['created']).replace(tzinfo=pytz.utc), + receipt_number=( + args['receipt_number'] + if args['receipt_number'] is not None else '' + ), + paid_at=datetime.utcfromtimestamp( + args['paid_at']).replace(tzinfo=pytz.utc), + period_start=datetime.utcfromtimestamp( + args['period_start']).replace(tzinfo=pytz.utc), + period_end=datetime.utcfromtimestamp( + args['period_end']).replace(tzinfo=pytz.utc), + billing_reason=args['billing_reason'], + discount=args['discount'], + total=args['total'], + lines_data_count=args['lines_data_count'], + invoice_id=args['invoice_id'], + lines_meta_data_csv=args['lines_meta_data_csv'], + stripe_customer=args['customer'], + subscription_ids_csv=args['subscription_ids_csv'], + ) + + instance.assign_permissions(instance.stripe_customer.user) return instance diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py index 7211465a..ec430485 100644 --- a/utils/stripe_utils.py +++ b/utils/stripe_utils.py @@ -155,7 +155,10 @@ class StripeUtils(object): 'invoice_id': invoice.id, 'lines_meta_data_csv': ','.join( [line.metadata.VM_ID if hasattr(line.metadata, 'VM_ID') else '' for line in invoice.lines.data] - ) + ), + 'subscription_ids_csv': ','.join( + [line.subscription if hasattr(line, 'subscription') else '' for line in invoice.lines.data] + ), } starting_after = invoice.id return_list.append(invoice_details) From 8e1e3e41576baa4c33bb7a23e733999632bba68c Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 09:12:14 +0200 Subject: [PATCH 035/795] Correct variable names --- hosting/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hosting/models.py b/hosting/models.py index c976c336..9b81addd 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -317,7 +317,8 @@ class MonthlyHostingBill(AssignPermissionsMixin, models.Model): lines_data_count=args['lines_data_count'], invoice_id=args['invoice_id'], lines_meta_data_csv=args['lines_meta_data_csv'], - stripe_customer=args['customer'], + customer=args['customer'], + order=args['order'], subscription_ids_csv=args['subscription_ids_csv'], ) From 2c3146111f5113116bf4dfa52772cbf26924b448 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 09:20:38 +0200 Subject: [PATCH 036/795] Fix getting subscription id --- utils/stripe_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py index ec430485..b43470fa 100644 --- a/utils/stripe_utils.py +++ b/utils/stripe_utils.py @@ -157,7 +157,7 @@ class StripeUtils(object): [line.metadata.VM_ID if hasattr(line.metadata, 'VM_ID') else '' for line in invoice.lines.data] ), 'subscription_ids_csv': ','.join( - [line.subscription if hasattr(line, 'subscription') else '' for line in invoice.lines.data] + [line.id if line.type == 'subscription' else '' for line in invoice.lines.data] ), } starting_after = invoice.id From a690ef421f8f55320c7c05121f715e1b67102347 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 09:24:25 +0200 Subject: [PATCH 037/795] Fix variable name --- hosting/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/models.py b/hosting/models.py index 9b81addd..fb2a805b 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -322,7 +322,7 @@ class MonthlyHostingBill(AssignPermissionsMixin, models.Model): subscription_ids_csv=args['subscription_ids_csv'], ) - instance.assign_permissions(instance.stripe_customer.user) + instance.assign_permissions(instance.customer.user) return instance From cc6afa8d2abd1d7a540fd9f471b09228cf4ceaaa Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 19:22:27 +0200 Subject: [PATCH 038/795] Fix datetime issue: pass unix timestamp instead of datetime --- hosting/management/commands/fetch_stripe_bills.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/management/commands/fetch_stripe_bills.py b/hosting/management/commands/fetch_stripe_bills.py index 8f35aa8c..e6dd9536 100644 --- a/hosting/management/commands/fetch_stripe_bills.py +++ b/hosting/management/commands/fetch_stripe_bills.py @@ -31,7 +31,7 @@ class Command(BaseCommand): # fetch only invoices which is created after # mhb.created, because we already have invoices till # this date - created_gt = {'gt': mhb.created} + created_gt = {'gt': mhb.created.timestamp()} all_invoices_response = stripe_utils.get_all_invoices( user.stripecustomer.stripe_id, From 5c31417a371c0376bc747208481da4e6ee9b69a0 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 19:34:57 +0200 Subject: [PATCH 039/795] Convert timestamp to int --- hosting/management/commands/fetch_stripe_bills.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/management/commands/fetch_stripe_bills.py b/hosting/management/commands/fetch_stripe_bills.py index e6dd9536..2a37ed61 100644 --- a/hosting/management/commands/fetch_stripe_bills.py +++ b/hosting/management/commands/fetch_stripe_bills.py @@ -31,7 +31,7 @@ class Command(BaseCommand): # fetch only invoices which is created after # mhb.created, because we already have invoices till # this date - created_gt = {'gt': mhb.created.timestamp()} + created_gt = {'gt': int(mhb.created.timestamp())} all_invoices_response = stripe_utils.get_all_invoices( user.stripecustomer.stripe_id, From 147fd0fe5edbb4566fcb043ef55decbd2792a44e Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 20:29:06 +0200 Subject: [PATCH 040/795] Add invoices.html --- hosting/templates/hosting/invoices.html | 57 +++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 hosting/templates/hosting/invoices.html diff --git a/hosting/templates/hosting/invoices.html b/hosting/templates/hosting/invoices.html new file mode 100644 index 00000000..96d9e9e3 --- /dev/null +++ b/hosting/templates/hosting/invoices.html @@ -0,0 +1,57 @@ +{% extends "hosting/base_short.html" %} +{% load staticfiles bootstrap3 humanize i18n %} + +{% block content %} +
+
+

{% trans "My Bills" %}

+ {% if messages %} +
+ {% for message in messages %} + {{ message }} + {% endfor %} +
+ {% endif %} +
+
+ + + + + + + + + + + + {% for order in orders %} + + + + + + + {% endfor %} + +
{% trans "Order Nr." %}{% trans "Date" %}{% trans "Amount" %}
{{ order.id }}{{ order.created_at | date:'Y-m-d h:i a' }}{{ order.price|floatformat:2|intcomma }} + {% trans 'See Invoice' %} +
+ + {% if is_paginated %} + + {% endif %} +
+{% endblock %} From de3734bf2000ba0c2db170342cb055f0a960b48b Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 20:29:33 +0200 Subject: [PATCH 041/795] Add total_in_chf utility method --- hosting/models.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hosting/models.py b/hosting/models.py index fb2a805b..b735bb8f 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -325,6 +325,15 @@ class MonthlyHostingBill(AssignPermissionsMixin, models.Model): instance.assign_permissions(instance.customer.user) return instance + def total_in_chf(self): + """ + Returns amount in chf. The total amount in this model is in cents. + Hence we multiply it by 0.01 to obtain the result + + :return: + """ + return self.total * 0.01 + class VMDetail(models.Model): user = models.ForeignKey(CustomUser) From 71832f8afc277cb1ebf1ea4d710d353d3d6651f3 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 20:31:24 +0200 Subject: [PATCH 042/795] invoices.html: Replace all order instances by invoice --- hosting/templates/hosting/invoices.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hosting/templates/hosting/invoices.html b/hosting/templates/hosting/invoices.html index 96d9e9e3..2fa2e3f4 100644 --- a/hosting/templates/hosting/invoices.html +++ b/hosting/templates/hosting/invoices.html @@ -18,20 +18,20 @@ - + - {% for order in orders %} + {% for invoice in invoices %} - - - + + + {% endfor %} From dbe3b2558cd132270fabd46ff89aef7d5f32f487 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 20:31:54 +0200 Subject: [PATCH 043/795] Create an InvoiceListView --- hosting/views.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index 32de4e54..043bad99 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -61,7 +61,7 @@ from .forms import ( from .mixins import ProcessVMSelectionMixin, HostingContextMixin from .models import ( HostingOrder, HostingBill, HostingPlan, UserHostingKey, VMDetail, - GenericProduct + GenericProduct, MonthlyHostingBill ) logger = logging.getLogger(__name__) @@ -1146,6 +1146,22 @@ class OrdersHostingListView(LoginRequiredMixin, ListView): return super(OrdersHostingListView, self).get(request, *args, **kwargs) +class InvoiceListView(OrdersHostingListView): + template_name = "hosting/invoices.html" + context_object_name = "invoices" + model = MonthlyHostingBill + ordering = '-created' + + def get_queryset(self): + user = self.request.user + self.queryset = MonthlyHostingBill.objects.filter(customer__user=user) + return super(InvoiceListView, self).get_queryset() + + @method_decorator(decorators) + def get(self, request, *args, **kwargs): + return super(InvoiceListView, self).get(request, *args, **kwargs) + + class OrdersHostingDeleteView(LoginRequiredMixin, DeleteView): login_url = reverse_lazy('hosting:login') success_url = reverse_lazy('hosting:orders') From def5a3a0115c70bbbdca5daef51a4fbaffe8462b Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 20:34:04 +0200 Subject: [PATCH 044/795] Add invoice urls --- hosting/urls.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hosting/urls.py b/hosting/urls.py index 32ef8400..3a0dd72f 100644 --- a/hosting/urls.py +++ b/hosting/urls.py @@ -8,7 +8,8 @@ from .views import ( MarkAsReadNotificationView, PasswordResetView, PasswordResetConfirmView, HostingPricingView, CreateVirtualMachinesView, HostingBillListView, HostingBillDetailView, SSHKeyDeleteView, SSHKeyCreateView, SSHKeyListView, - SSHKeyChoiceView, DashboardView, SettingsView, ResendActivationEmailView + SSHKeyChoiceView, DashboardView, SettingsView, ResendActivationEmailView, + InvoiceListView ) @@ -22,10 +23,13 @@ urlpatterns = [ url(r'payment/?$', PaymentVMView.as_view(), name='payment'), url(r'settings/?$', SettingsView.as_view(), name='settings'), url(r'orders/?$', OrdersHostingListView.as_view(), name='orders'), + url(r'invoices/?$', InvoiceListView.as_view(), name='invoices'), url(r'order-confirmation/?$', OrdersHostingDetailView.as_view(), name='order-confirmation'), url(r'orders/(?P\d+)/?$', OrdersHostingDetailView.as_view(), name='orders'), + url(r'invoices/(?P\d+)/?$', OrdersHostingDetailView.as_view(), + name='invoices'), url(r'bills/?$', HostingBillListView.as_view(), name='bills'), url(r'bills/(?P\d+)/?$', HostingBillDetailView.as_view(), name='bills'), From e843a6f85753292e803dc6d373e7ca36490094e5 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 21:16:19 +0200 Subject: [PATCH 045/795] Make invoicelistview not inherit OrderHostingListView --- hosting/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index 043bad99..47456574 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1146,8 +1146,9 @@ class OrdersHostingListView(LoginRequiredMixin, ListView): return super(OrdersHostingListView, self).get(request, *args, **kwargs) -class InvoiceListView(OrdersHostingListView): +class InvoiceListView(LoginRequiredMixin, ListView): template_name = "hosting/invoices.html" + login_url = reverse_lazy('hosting:login') context_object_name = "invoices" model = MonthlyHostingBill ordering = '-created' From 247bbe622fb47352e457faa5f6a370199fb941b4 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 21:29:49 +0200 Subject: [PATCH 046/795] Add missing invoice_number argument to MHB create --- hosting/models.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hosting/models.py b/hosting/models.py index b735bb8f..5b48abbf 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -305,6 +305,10 @@ class MonthlyHostingBill(AssignPermissionsMixin, models.Model): args['receipt_number'] if args['receipt_number'] is not None else '' ), + invoice_number=( + args['invoice_number'] + if args['invoice_number'] is not None else '' + ), paid_at=datetime.utcfromtimestamp( args['paid_at']).replace(tzinfo=pytz.utc), period_start=datetime.utcfromtimestamp( From ba9e5548811bb1e4160ac777b294b5268c9e9fd4 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 21:52:07 +0200 Subject: [PATCH 047/795] Implement get_object for invoice detail + url fix --- hosting/urls.py | 4 ++-- hosting/views.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/hosting/urls.py b/hosting/urls.py index 3a0dd72f..3f5a6f50 100644 --- a/hosting/urls.py +++ b/hosting/urls.py @@ -9,7 +9,7 @@ from .views import ( HostingPricingView, CreateVirtualMachinesView, HostingBillListView, HostingBillDetailView, SSHKeyDeleteView, SSHKeyCreateView, SSHKeyListView, SSHKeyChoiceView, DashboardView, SettingsView, ResendActivationEmailView, - InvoiceListView + InvoiceListView, InvoiceDetailView ) @@ -28,7 +28,7 @@ urlpatterns = [ name='order-confirmation'), url(r'orders/(?P\d+)/?$', OrdersHostingDetailView.as_view(), name='orders'), - url(r'invoices/(?P\d+)/?$', OrdersHostingDetailView.as_view(), + url(r'invoices/(?P[-\w]+)/?$', InvoiceDetailView.as_view(), name='invoices'), url(r'bills/?$', HostingBillListView.as_view(), name='bills'), url(r'bills/(?P\d+)/?$', HostingBillDetailView.as_view(), diff --git a/hosting/views.py b/hosting/views.py index 47456574..17f63039 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1163,6 +1163,35 @@ class InvoiceListView(LoginRequiredMixin, ListView): return super(InvoiceListView, self).get(request, *args, **kwargs) +class InvoiceDetailView(LoginRequiredMixin, DetailView): + template_name = "hosting/invoice-detail.html" + context_object_name = "invoice" + login_url = reverse_lazy('hosting:login') + permission_required = ['view_monthlyhostingbill'] + model = MonthlyHostingBill + + def get_object(self, queryset=None): + invoice_id = self.kwargs.get('invoice_id') + try: + invoice_obj = MonthlyHostingBill.objects.get(invoice_number=invoice_id) + logger.debug("Found MHB for id {invoice_id}".format( + invoice_id=invoice_id + )) + if self.request.user.has_perm( + self.permission_required[0], invoice_obj + ) or self.request.user.email == settings.ADMIN_EMAIL: + logger.debug("User has permission to invoice_obj") + else: + logger.error("User does not have permission to access") + invoice_obj = None + except HostingOrder.DoesNotExist: + logger.debug("MHB not found for id {invoice_id}".format( + invoice_id=invoice_id + )) + invoice_obj = None + return invoice_obj + + class OrdersHostingDeleteView(LoginRequiredMixin, DeleteView): login_url = reverse_lazy('hosting:login') success_url = reverse_lazy('hosting:orders') From 94586c854a47f7569f3ef35371e50fec9d589414 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 22:48:23 +0200 Subject: [PATCH 048/795] Add invoice detail --- hosting/templates/hosting/invoice_detail.html | 235 ++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 hosting/templates/hosting/invoice_detail.html diff --git a/hosting/templates/hosting/invoice_detail.html b/hosting/templates/hosting/invoice_detail.html new file mode 100644 index 00000000..ff6ec31d --- /dev/null +++ b/hosting/templates/hosting/invoice_detail.html @@ -0,0 +1,235 @@ +{% extends "hosting/base_short.html" %} +{% load staticfiles bootstrap3 humanize i18n custom_tags %} + + +{% block content %} +
+ {% if messages %} +
+ {% for message in messages %} + {{ message }} + {% endfor %} +
+ {% endif %} + {% if not error %} +
+

+ {% + blocktrans with page_header_text=page_header_text|default:"Invoice" + %}{{page_header_text}}{% endblocktrans %} +

+ {% if invoice %} +
+ + +
+ {% endif %} +
+
+ {% if invoice %} +

+ {% trans "Invoice #" %} {{invoice.invoice_number}} +

+ {% endif %} +

+ {% trans "Date" %}: + + {% if invoice %} + {{invoice.paid_at|date:'Y-m-d h:i a'}} + {% else %} + {% now "Y-m-d h:i a" %} + {% endif %} + +

+ {% if invoice and vm %} +

+ {% trans "Status" %}: + + {% if vm.terminated_at %} + {% trans "Terminated" %} + {% elif invoice.order.status == 'Approved' %} + {% trans "Approved" %} + {% else %} + {% trans "Declined" %} + {% endif %} + +

+ {% endif %} +
+
+
+

{% trans "Billed to" %}:

+

+ {% if invoice.order %} + {{user.name}}
+ {{invoice.order.billing_address.street_address}}, + {{invoice.order.billing_address.postal_code}}
+ {{invoice.order.billing_address.city}}, + {{invoice.order.billing_address.country}} + {% endif %} +

+
+
+
+
+

{% trans "Payment method" %}:

+

+ {% if invoice.order %} + {{invoice.order.cc_brand}} {% trans "ending in" %} **** + {{invoice.order.last4}}
+ {{user.email}} + {% endif %} +

+
+
+
+

{% trans "Invoice summary" %}

+ {% if vm %} +

+ {% trans "Product" %}:  + {% if vm.name %} + {{ vm.name }} + {% endif %} +

+
+
+ {% if period_start %} +

+ {% trans "Period" %}: + + {{ period_start|date:'Y-m-d h:i a' }} - {{ period_end|date:'Y-m-d h:i a' }} + +

+ {% endif %} +

+ {% trans "Cores" %}: + {% if vm.cores %} + {{vm.cores|floatformat}} + {% else %} + {{vm.cpu|floatformat}} + {% endif %} +

+

+ {% trans "Memory" %}: + {{vm.memory}} GB +

+

+ {% trans "Disk space" %}: + {{vm.disk_size}} GB +

+
+
+
+
+ {% if vm.vat > 0 or vm.discount.amount > 0 %} +
+
+ {% if vm.vat > 0 %} +

+ {% trans "Subtotal" %} + {{vm.price|floatformat:2|intcomma}} + CHF +

+

+ {% trans "VAT" %} ({{ + vm.vat_percent|floatformat:2|intcomma }}%) + + {{vm.vat|floatformat:2|intcomma}} + CHF +

+ {% endif %} + {% if vm.discount.amount > 0 %} +

+ {%trans "Discount" as discount_name %} + {{ vm.discount.name|default:discount_name + }} + - {{ vm.discount.amount + }} CHF +

+ {% endif %} +
+
+
+
+
+ {% endif %} +
+

+ {% trans "Total" %} + {% if vm.total_price + %}{{vm.total_price|floatformat:2|intcomma}}{% else + %}{{vm.price|floatformat:2|intcomma}}{% endif %} + CHF +

+
+
+ {% else %} +

+ {% trans "Product" %}:  + {{ product_name }} +

+
+
+

+ {% trans "Amount" %}: + {{total_in_chf|floatformat:2|intcomma}} + CHF +

+ {% if invoice.order.generic_payment_description %} +

+ {% trans "Description" %}: + {{invoice.order.generic_payment_description}} +

+ {% endif %} + {% if invoice.order.subscription_id %} +

+ {% trans "Recurring" %}: + {{invoice.order.created_at|date:'d'|ordinal}} + {% trans "of every month" %} +

+ {% endif %} +
+
+ {% endif %} +
+
+
+ + {% endif %} +
+ + + + +{%endblock%} + +{% block js_extra %} +{% if invoice.order %} + + + + +{% endif %} +{% endblock js_extra %} From d37a2de6eb5dec8c935b7b4692d87875cf60775a Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 22:48:56 +0200 Subject: [PATCH 049/795] Add utility functions --- hosting/models.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/hosting/models.py b/hosting/models.py index 5b48abbf..d58e2fce 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -338,6 +338,33 @@ class MonthlyHostingBill(AssignPermissionsMixin, models.Model): """ return self.total * 0.01 + def discount_in_chf(self): + """ + Returns discount in chf. + + :return: + """ + return self.discount * 0.01 + + def get_vm_id(self): + """ + Returns the VM_ID metadata if set in this MHB else returns None + :return: + """ + return_value = None + if len(self.lines_meta_data_csv) > 0: + vm_ids = [vm_id.strip() for vm_id in + self.lines_meta_data_csv.split(",")] + if len(vm_ids) == 1: + return vm_ids[0] + else: + logger.debug( + "More than one VM_ID" + "for MonthlyHostingBill {}".format(self.invoice_id) + ) + logger.debug("VM_IDS=".format(','.join(vm_ids))) + return return_value + class VMDetail(models.Model): user = models.ForeignKey(CustomUser) From ba6fa531db76bb7a8683f45cf9b76fd973f4a393 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 22:49:25 +0200 Subject: [PATCH 050/795] Correct the name of the layout --- hosting/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index 17f63039..5a8f45c7 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1164,7 +1164,7 @@ class InvoiceListView(LoginRequiredMixin, ListView): class InvoiceDetailView(LoginRequiredMixin, DetailView): - template_name = "hosting/invoice-detail.html" + template_name = "hosting/invoice_detail.html" context_object_name = "invoice" login_url = reverse_lazy('hosting:login') permission_required = ['view_monthlyhostingbill'] From 7de2129a0053fd799e568a7508b791a2345d2ee4 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 22:49:45 +0200 Subject: [PATCH 051/795] Implement get invoice --- hosting/views.py | 76 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/hosting/views.py b/hosting/views.py index 5a8f45c7..49c78b7e 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1191,6 +1191,82 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView): invoice_obj = None return invoice_obj + def get_context_data(self, **kwargs): + # Get context + context = super(InvoiceDetailView, self).get_context_data(**kwargs) + obj = self.get_object() + + if obj is not None: + vm_id = obj.get_vm_id() + try: + # Try to get vm details from database + vm_detail = VMDetail.objects.get(vm_id=vm_id) + context['vm'] = vm_detail.__dict__ + context['vm']['name'] = '{}-{}'.format( + context['vm']['configuration'], context['vm']['vm_id']) + price, vat, vat_percent, discount = 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']['discount'] = discount + context['vm']['vat_percent'] = vat_percent + context['vm']['total_price'] = price + vat - discount['amount'] + except VMDetail.DoesNotExist: + # fallback to get it from the infrastructure + try: + manager = OpenNebulaManager( + email=self.request.email, + password=self.request.password + ) + vm = manager.get_vm(vm_id) + context['vm'] = VirtualMachineSerializer(vm).data + price, vat, vat_percent, discount = 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']['discount'] = discount + context['vm']['vat_percent'] = vat_percent + context['vm']['total_price'] = ( + price + vat - discount['amount'] + ) + except WrongIdError: + logger.error("WrongIdError while accessing " + "invoice {}".format(obj.invoice_id)) + messages.error( + self.request, + _('The VM you are looking for is unavailable at the ' + 'moment. Please contact Data Center Light support.') + ) + self.kwargs['error'] = 'WrongIdError' + context['error'] = 'WrongIdError' + return context + + # add context params from monthly hosting bill + context['period_start'] = obj.period_start + context['period_end'] = obj.period_end + context['paid_at'] = obj.paid_at + context['total_in_chf'] = obj.total_in_chf() + context['invoice_number'] = obj.invoice_number + context['discount_on_stripe'] = obj.discount_in_chf() + return context + else: + raise Http404 + + @method_decorator(decorators) + def get(self, request, *args, **kwargs): + context = self.get_context_data() + return self.render_to_response(context) + class OrdersHostingDeleteView(LoginRequiredMixin, DeleteView): login_url = reverse_lazy('hosting:login') From d07f3d7eba1d54c552bfaea0d56d94b2220bec62 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 22:59:01 +0200 Subject: [PATCH 052/795] Add missing object param --- hosting/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index 49c78b7e..39e2a7a9 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1264,7 +1264,7 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView): @method_decorator(decorators) def get(self, request, *args, **kwargs): - context = self.get_context_data() + context = self.get_context_data(object=self.get_object()) return self.render_to_response(context) From 76e3d951354f4d2278f322792be90d221566e1f3 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 22:59:39 +0200 Subject: [PATCH 053/795] Use invoice_number of invoice pk --- hosting/templates/hosting/invoices.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/templates/hosting/invoices.html b/hosting/templates/hosting/invoices.html index 2fa2e3f4..6a0aeb41 100644 --- a/hosting/templates/hosting/invoices.html +++ b/hosting/templates/hosting/invoices.html @@ -31,7 +31,7 @@
{% endfor %} From 3ed5823c93651e25bf1e0749d5b0045b2b0459fe Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 23:04:35 +0200 Subject: [PATCH 054/795] Add missing self.object initializer --- hosting/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/hosting/views.py b/hosting/views.py index 39e2a7a9..3a8997a9 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1264,6 +1264,7 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView): @method_decorator(decorators) def get(self, request, *args, **kwargs): + self.object = self.get_object() context = self.get_context_data(object=self.get_object()) return self.render_to_response(context) From ef09ae4dab1b355630af25edd4aeefa5a0f6062a Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 23:07:37 +0200 Subject: [PATCH 055/795] Obtaing pricing from order --- hosting/views.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 3a8997a9..12b0027f 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1208,8 +1208,8 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView): 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') + pricing_name=(obj.order.vm_pricing.name + if obj.order.vm_pricing else 'default') ) context['vm']['vat'] = vat context['vm']['price'] = price @@ -1229,8 +1229,8 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView): 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') + pricing_name=(obj.order.vm_pricing.name + if obj.order.vm_pricing else 'default') ) context['vm']['vat'] = vat context['vm']['price'] = price From ddd3cebc39f49aca62973921be9f7d716001332b Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 23:09:57 +0200 Subject: [PATCH 056/795] Fix blocktrans reformatted mistakenly --- hosting/templates/hosting/invoice_detail.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hosting/templates/hosting/invoice_detail.html b/hosting/templates/hosting/invoice_detail.html index ff6ec31d..8a094519 100644 --- a/hosting/templates/hosting/invoice_detail.html +++ b/hosting/templates/hosting/invoice_detail.html @@ -14,9 +14,8 @@ {% if not error %}

- {% - blocktrans with page_header_text=page_header_text|default:"Invoice" - %}{{page_header_text}}{% endblocktrans %} + + {% blocktrans with page_header_text=page_header_text|default:"Invoice" %}{{page_header_text}}{% endblocktrans %}

{% if invoice %}
From 47422a99afa1b6cccd0533915d2fefe565c1bbf7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 23:11:59 +0200 Subject: [PATCH 057/795] Fix more autoformatting related errors --- hosting/templates/hosting/invoice_detail.html | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/hosting/templates/hosting/invoice_detail.html b/hosting/templates/hosting/invoice_detail.html index 8a094519..22d1d87a 100644 --- a/hosting/templates/hosting/invoice_detail.html +++ b/hosting/templates/hosting/invoice_detail.html @@ -207,7 +207,6 @@ CHE-156.970.649 MWST
- {% endif %}
@@ -216,10 +215,7 @@
{%endblock%} From d00e84a4b69508904225ce69934a671c5cf05db5 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 23:24:56 +0200 Subject: [PATCH 058/795] Fix bug related to proper alignment --- hosting/views.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 12b0027f..ce73ad3c 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1251,14 +1251,14 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView): context['error'] = 'WrongIdError' return context - # add context params from monthly hosting bill - context['period_start'] = obj.period_start - context['period_end'] = obj.period_end - context['paid_at'] = obj.paid_at - context['total_in_chf'] = obj.total_in_chf() - context['invoice_number'] = obj.invoice_number - context['discount_on_stripe'] = obj.discount_in_chf() - return context + # add context params from monthly hosting bill + context['period_start'] = obj.period_start + context['period_end'] = obj.period_end + context['paid_at'] = obj.paid_at + context['total_in_chf'] = obj.total_in_chf() + context['invoice_number'] = obj.invoice_number + context['discount_on_stripe'] = obj.discount_in_chf() + return context else: raise Http404 From 903fee4db198be5a4e35ba5c068a9ea97cf558f2 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 23:31:52 +0200 Subject: [PATCH 059/795] Fix more autoformatting issues --- hosting/templates/hosting/invoice_detail.html | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/hosting/templates/hosting/invoice_detail.html b/hosting/templates/hosting/invoice_detail.html index 22d1d87a..9c300b3f 100644 --- a/hosting/templates/hosting/invoice_detail.html +++ b/hosting/templates/hosting/invoice_detail.html @@ -137,8 +137,7 @@ CHF

- {% trans "VAT" %} ({{ - vm.vat_percent|floatformat:2|intcomma }}%) + {% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%) {{vm.vat|floatformat:2|intcomma}} CHF @@ -147,10 +146,8 @@ {% if vm.discount.amount > 0 %}

{%trans "Discount" as discount_name %} - {{ vm.discount.name|default:discount_name - }} - - {{ vm.discount.amount - }} CHF + {{ vm.discount.name|default:discount_name }} + - {{ vm.discount.amount }} CHF

{% endif %} @@ -162,9 +159,7 @@

{% trans "Total" %} - {% if vm.total_price - %}{{vm.total_price|floatformat:2|intcomma}}{% else - %}{{vm.price|floatformat:2|intcomma}}{% endif %} + {% if vm.total_price %}{{vm.total_price|floatformat:2|intcomma}}{% else %}{{vm.price|floatformat:2|intcomma}}{% endif %} CHF

From f1a7958f03e3c61eef6e4b3dd4028066c273c353 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 23:34:26 +0200 Subject: [PATCH 060/795] Use correct class --- hosting/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index ce73ad3c..34e720f6 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1184,7 +1184,7 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView): else: logger.error("User does not have permission to access") invoice_obj = None - except HostingOrder.DoesNotExist: + except MonthlyHostingBill.DoesNotExist: logger.debug("MHB not found for id {invoice_id}".format( invoice_id=invoice_id )) From baf62f1924c4079bb01bbb8fb0d629223fdd4661 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 23:54:52 +0200 Subject: [PATCH 061/795] Simplify showing total price --- hosting/templates/hosting/invoice_detail.html | 6 ++---- hosting/views.py | 6 ++++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hosting/templates/hosting/invoice_detail.html b/hosting/templates/hosting/invoice_detail.html index 9c300b3f..80226123 100644 --- a/hosting/templates/hosting/invoice_detail.html +++ b/hosting/templates/hosting/invoice_detail.html @@ -139,8 +139,7 @@

{% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%) - {{vm.vat|floatformat:2|intcomma}} - CHF + {{vm.vat|floatformat:2|intcomma}} CHF

{% endif %} {% if vm.discount.amount > 0 %} @@ -159,8 +158,7 @@

{% trans "Total" %} - {% if vm.total_price %}{{vm.total_price|floatformat:2|intcomma}}{% else %}{{vm.price|floatformat:2|intcomma}}{% endif %} - CHF + {{total_in_chf}} CHF

diff --git a/hosting/views.py b/hosting/views.py index 34e720f6..af01ae86 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1173,7 +1173,9 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView): def get_object(self, queryset=None): invoice_id = self.kwargs.get('invoice_id') try: - invoice_obj = MonthlyHostingBill.objects.get(invoice_number=invoice_id) + invoice_obj = MonthlyHostingBill.objects.get( + invoice_number=invoice_id + ) logger.debug("Found MHB for id {invoice_id}".format( invoice_id=invoice_id )) @@ -1184,7 +1186,7 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView): else: logger.error("User does not have permission to access") invoice_obj = None - except MonthlyHostingBill.DoesNotExist: + except MonthlyHostingBill.DoesNotExist as dne: logger.debug("MHB not found for id {invoice_id}".format( invoice_id=invoice_id )) From 13f84a8580cda21cf55675fb1cf1e6d3a41ee248 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 4 Apr 2019 00:05:20 +0200 Subject: [PATCH 062/795] Add missing endif --- hosting/templates/hosting/invoice_detail.html | 1 + 1 file changed, 1 insertion(+) diff --git a/hosting/templates/hosting/invoice_detail.html b/hosting/templates/hosting/invoice_detail.html index 80226123..0a2473e3 100644 --- a/hosting/templates/hosting/invoice_detail.html +++ b/hosting/templates/hosting/invoice_detail.html @@ -200,6 +200,7 @@ CHE-156.970.649 MWST
+ {% endif %}
From ef1bdee9a7da7e341f6e44d7046f7b544cc0f5cd Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 4 Apr 2019 00:05:45 +0200 Subject: [PATCH 063/795] Remove more autoformatting --- hosting/templates/hosting/invoice_detail.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hosting/templates/hosting/invoice_detail.html b/hosting/templates/hosting/invoice_detail.html index 0a2473e3..675962fa 100644 --- a/hosting/templates/hosting/invoice_detail.html +++ b/hosting/templates/hosting/invoice_detail.html @@ -204,8 +204,7 @@
-{%endblock%} \ No newline at end of file +{%endblock%} diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 301049aa..078f64ac 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -254,9 +254,6 @@ class PaymentOrderView(FormView): billing_address_form = BillingAddressForm( instance=self.request.user.billing_addresses.first() ) - billing_address_form.fields['vat_number'].initial = ( - self.request.user.vat_number - ) user = self.request.user if hasattr(user, 'stripecustomer'): stripe_customer = user.stripecustomer @@ -491,9 +488,6 @@ class PaymentOrderView(FormView): customer = StripeCustomer.get_or_create( email=this_user.get('email'), token=token ) - request.user.vat_number = address_form.cleaned_data.get( - "vat_number") - request.user.save() else: user_email = address_form.cleaned_data.get('email') user_name = address_form.cleaned_data.get('name') @@ -949,11 +943,6 @@ class OrderConfirmationView(DetailView, FormView): 'user': custom_user.id }) - # Customer is created, we save his VAT Number - custom_user.vat_number = request.session.get( - 'billing_address_data').get("vat_number") - custom_user.save() - if 'generic_payment_type' in request.session: stripe_cus = StripeCustomer.objects.filter( stripe_id=stripe_api_cus_id @@ -964,6 +953,7 @@ class OrderConfirmationView(DetailView, FormView): city=billing_address_data['city'], postal_code=billing_address_data['postal_code'], country=billing_address_data['country'] + vat_number=billing_address_data['vat_number'] ) billing_address.save() diff --git a/hosting/templates/hosting/invoice_detail.html b/hosting/templates/hosting/invoice_detail.html index 3463e505..e5714b82 100644 --- a/hosting/templates/hosting/invoice_detail.html +++ b/hosting/templates/hosting/invoice_detail.html @@ -70,8 +70,8 @@ {{invoice.order.billing_address.postal_code}}
{{invoice.order.billing_address.city}}, {{invoice.order.billing_address.country}} - {% if invoice.customer.user.vat_number %} -
{% trans "VAT Number" %} {{invoice.customer.user.vat_number}} + {% if invoice.order.billing_address.vat_number %} +
{% trans "VAT Number" %} {{invoice.order.billing_address.vat_number}} {% endif %} {% endif %}

diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 0ce72fa3..2ad17276 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -67,8 +67,8 @@ {{billing_address.cardholder_name}}
{{billing_address.street_address}}, {{billing_address.postal_code}}
{{billing_address.city}}, {{billing_address.country}} - {% if user.vat_number %} -
{% trans "VAT Number" %} {{user.vat_number}} + {% if billing_address.vat_number %} +
{% trans "VAT Number" %} {{billing_address.vat_number}} {% endif %} {% endwith %} {% endif %} diff --git a/hosting/views.py b/hosting/views.py index 39b43ad8..3f89496b 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -549,12 +549,9 @@ class SettingsView(LoginRequiredMixin, FormView): Check if the user already saved contact details. If so, then show the form populated with those details, to let user change them. """ - - bill_form = form_class( + return form_class( instance=self.request.user.billing_addresses.first(), **self.get_form_kwargs()) - bill_form.fields['vat_number'].initial = self.request.user.vat_number - return bill_form def get_context_data(self, **kwargs): context = super(SettingsView, self).get_context_data(**kwargs) @@ -630,9 +627,6 @@ class SettingsView(LoginRequiredMixin, FormView): instance=self.request.user.billing_addresses.first(), data=billing_address_data) billing_address_user_form.save() - self.request.user.vat_number = billing_address_data.get( - "vat_number") - self.request.user.save() msg = _("Billing address updated successfully") messages.add_message(request, messages.SUCCESS, msg) else: @@ -705,7 +699,7 @@ class PaymentVMView(LoginRequiredMixin, FormView): 'city': current_billing_address.city, 'postal_code': current_billing_address.postal_code, 'country': current_billing_address.country, - 'vat_number': self.request.user.vat_number + 'vat_number': current_billing_address.vat_number } }) return form_kwargs @@ -790,8 +784,6 @@ class PaymentVMView(LoginRequiredMixin, FormView): reverse('hosting:payment') + '#payment_error') request.session['token'] = token request.session['billing_address_data'] = billing_address_data - owner.vat_number = billing_address_data.get("vat_number") - owner.save() self.request.session['order_confirm_url'] = "{url}?{query_params}".format( url=reverse('hosting:order-confirmation'), query_params='page=payment') diff --git a/membership/models.py b/membership/models.py index 34ef73cc..80aaf408 100644 --- a/membership/models.py +++ b/membership/models.py @@ -117,8 +117,6 @@ class CustomUser(AbstractBaseUser, PermissionsMixin): name = models.CharField(max_length=50, validators=[validate_name]) email = models.EmailField(unique=True) username = models.CharField(max_length=60, unique=True, null=True) - vat_number = models.CharField(max_length=100, default="") - validated = models.IntegerField(choices=VALIDATED_CHOICES, default=0) in_ldap = models.BooleanField(default=False) # By default, we initialize the validation_slug with appropriate value diff --git a/utils/forms.py b/utils/forms.py index 9855e367..71a675bc 100644 --- a/utils/forms.py +++ b/utils/forms.py @@ -120,7 +120,6 @@ class EditCreditCardForm(forms.Form): class BillingAddressForm(forms.ModelForm): token = forms.CharField(widget=forms.HiddenInput(), required=False) card = forms.CharField(widget=forms.HiddenInput(), required=False) - vat_number = forms.CharField(max_length=100) class Meta: model = BillingAddress diff --git a/utils/models.py b/utils/models.py index 8cd529e0..e70dea5c 100644 --- a/utils/models.py +++ b/utils/models.py @@ -13,6 +13,7 @@ class BaseBillingAddress(models.Model): city = models.CharField(max_length=50) postal_code = models.CharField(max_length=50) country = CountryField() + vat_number = models.CharField(max_length=100) class Meta: abstract = True From b919b6cfbdca142da5a5fd01ad7e99e7646d4842 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 21 Dec 2019 08:51:11 +0530 Subject: [PATCH 396/795] Fix wrong indentation --- datacenterlight/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 078f64ac..0763e259 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -952,8 +952,8 @@ class OrderConfirmationView(DetailView, FormView): street_address=billing_address_data['street_address'], city=billing_address_data['city'], postal_code=billing_address_data['postal_code'], - country=billing_address_data['country'] - vat_number=billing_address_data['vat_number'] + country=billing_address_data['country'], + vat_number=billing_address_data['vat_number'] ) billing_address.save() From 202a514b1b00e90eda63b51df614585ea07aa201 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 21 Dec 2019 08:51:43 +0530 Subject: [PATCH 397/795] Set empty default value for vat_number --- utils/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/models.py b/utils/models.py index e70dea5c..3bf5020f 100644 --- a/utils/models.py +++ b/utils/models.py @@ -13,7 +13,7 @@ class BaseBillingAddress(models.Model): city = models.CharField(max_length=50) postal_code = models.CharField(max_length=50) country = CountryField() - vat_number = models.CharField(max_length=100) + vat_number = models.CharField(max_length=100, default="") class Meta: abstract = True From 2d66ae6783eb208898d47d43d627310b2272302c Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 21 Dec 2019 08:52:06 +0530 Subject: [PATCH 398/795] Improve BillingAddress string representation Include VAT number if available --- utils/models.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/utils/models.py b/utils/models.py index 3bf5020f..b6c0ef88 100644 --- a/utils/models.py +++ b/utils/models.py @@ -21,10 +21,16 @@ class BaseBillingAddress(models.Model): class BillingAddress(BaseBillingAddress): def __str__(self): - return "%s, %s, %s, %s, %s" % ( - self.cardholder_name, self.street_address, self.city, - self.postal_code, self.country - ) + if self.vat_number: + return "%s, %s, %s, %s, %s, %s" % ( + self.cardholder_name, self.street_address, self.city, + self.postal_code, self.country, self.vat_number + ) + else: + return "%s, %s, %s, %s, %s" % ( + self.cardholder_name, self.street_address, self.city, + self.postal_code, self.country + ) class UserBillingAddress(BaseBillingAddress): From e3078f3ea9c14d0c98bb9b9a0a9f668d8a000c2f Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 21 Dec 2019 08:52:43 +0530 Subject: [PATCH 399/795] Add migration --- utils/migrations/0007_auto_20191221_0319.py | 36 +++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 utils/migrations/0007_auto_20191221_0319.py diff --git a/utils/migrations/0007_auto_20191221_0319.py b/utils/migrations/0007_auto_20191221_0319.py new file mode 100644 index 00000000..b9fe9b6d --- /dev/null +++ b/utils/migrations/0007_auto_20191221_0319.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2019-12-21 03:19 +from __future__ import unicode_literals + +from django.db import migrations, models +import utils.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('utils', '0006_auto_20170810_1742'), + ] + + operations = [ + migrations.AddField( + model_name='billingaddress', + name='vat_number', + field=models.CharField(default='', max_length=100), + ), + migrations.AddField( + model_name='userbillingaddress', + name='vat_number', + field=models.CharField(default='', max_length=100), + ), + migrations.AlterField( + model_name='billingaddress', + name='country', + field=utils.fields.CountryField(choices=[('AD', 'Andorra'), ('AE', 'United Arab Emirates'), ('AF', 'Afghanistan'), ('AG', 'Antigua & Barbuda'), ('AI', 'Anguilla'), ('AL', 'Albania'), ('AM', 'Armenia'), ('AN', 'Netherlands Antilles'), ('AO', 'Angola'), ('AQ', 'Antarctica'), ('AR', 'Argentina'), ('AS', 'American Samoa'), ('AT', 'Austria'), ('AU', 'Australia'), ('AW', 'Aruba'), ('AZ', 'Azerbaijan'), ('BA', 'Bosnia and Herzegovina'), ('BB', 'Barbados'), ('BD', 'Bangladesh'), ('BE', 'Belgium'), ('BF', 'Burkina Faso'), ('BG', 'Bulgaria'), ('BH', 'Bahrain'), ('BI', 'Burundi'), ('BJ', 'Benin'), ('BM', 'Bermuda'), ('BN', 'Brunei Darussalam'), ('BO', 'Bolivia'), ('BR', 'Brazil'), ('BS', 'Bahama'), ('BT', 'Bhutan'), ('BV', 'Bouvet Island'), ('BW', 'Botswana'), ('BY', 'Belarus'), ('BZ', 'Belize'), ('CA', 'Canada'), ('CC', 'Cocos (Keeling) Islands'), ('CF', 'Central African Republic'), ('CG', 'Congo'), ('CH', 'Switzerland'), ('CI', 'Ivory Coast'), ('CK', 'Cook Iislands'), ('CL', 'Chile'), ('CM', 'Cameroon'), ('CN', 'China'), ('CO', 'Colombia'), ('CR', 'Costa Rica'), ('CU', 'Cuba'), ('CV', 'Cape Verde'), ('CX', 'Christmas Island'), ('CY', 'Cyprus'), ('CZ', 'Czech Republic'), ('DE', 'Germany'), ('DJ', 'Djibouti'), ('DK', 'Denmark'), ('DM', 'Dominica'), ('DO', 'Dominican Republic'), ('DZ', 'Algeria'), ('EC', 'Ecuador'), ('EE', 'Estonia'), ('EG', 'Egypt'), ('EH', 'Western Sahara'), ('ER', 'Eritrea'), ('ES', 'Spain'), ('ET', 'Ethiopia'), ('FI', 'Finland'), ('FJ', 'Fiji'), ('FK', 'Falkland Islands (Malvinas)'), ('FM', 'Micronesia'), ('FO', 'Faroe Islands'), ('FR', 'France'), ('FX', 'France, Metropolitan'), ('GA', 'Gabon'), ('GB', 'United Kingdom (Great Britain)'), ('GD', 'Grenada'), ('GE', 'Georgia'), ('GF', 'French Guiana'), ('GH', 'Ghana'), ('GI', 'Gibraltar'), ('GL', 'Greenland'), ('GM', 'Gambia'), ('GN', 'Guinea'), ('GP', 'Guadeloupe'), ('GQ', 'Equatorial Guinea'), ('GR', 'Greece'), ('GS', 'South Georgia and the South Sandwich Islands'), ('GT', 'Guatemala'), ('GU', 'Guam'), ('GW', 'Guinea-Bissau'), ('GY', 'Guyana'), ('HK', 'Hong Kong'), ('HM', 'Heard & McDonald Islands'), ('HN', 'Honduras'), ('HR', 'Croatia'), ('HT', 'Haiti'), ('HU', 'Hungary'), ('ID', 'Indonesia'), ('IE', 'Ireland'), ('IL', 'Israel'), ('IN', 'India'), ('IO', 'British Indian Ocean Territory'), ('IQ', 'Iraq'), ('IR', 'Islamic Republic of Iran'), ('IS', 'Iceland'), ('IT', 'Italy'), ('JM', 'Jamaica'), ('JO', 'Jordan'), ('JP', 'Japan'), ('KE', 'Kenya'), ('KG', 'Kyrgyzstan'), ('KH', 'Cambodia'), ('KI', 'Kiribati'), ('KM', 'Comoros'), ('KN', 'St. Kitts and Nevis'), ('KP', "Korea, Democratic People's Republic of"), ('KR', 'Korea, Republic of'), ('KW', 'Kuwait'), ('KY', 'Cayman Islands'), ('KZ', 'Kazakhstan'), ('LA', "Lao People's Democratic Republic"), ('LB', 'Lebanon'), ('LC', 'Saint Lucia'), ('LI', 'Liechtenstein'), ('LK', 'Sri Lanka'), ('LR', 'Liberia'), ('LS', 'Lesotho'), ('LT', 'Lithuania'), ('LU', 'Luxembourg'), ('LV', 'Latvia'), ('LY', 'Libyan Arab Jamahiriya'), ('MA', 'Morocco'), ('MC', 'Monaco'), ('MD', 'Moldova, Republic of'), ('MG', 'Madagascar'), ('MH', 'Marshall Islands'), ('ML', 'Mali'), ('MN', 'Mongolia'), ('MM', 'Myanmar'), ('MO', 'Macau'), ('MP', 'Northern Mariana Islands'), ('MQ', 'Martinique'), ('MR', 'Mauritania'), ('MS', 'Monserrat'), ('MT', 'Malta'), ('MU', 'Mauritius'), ('MV', 'Maldives'), ('MW', 'Malawi'), ('MX', 'Mexico'), ('MY', 'Malaysia'), ('MZ', 'Mozambique'), ('NA', 'Namibia'), ('NC', 'New Caledonia'), ('NE', 'Niger'), ('NF', 'Norfolk Island'), ('NG', 'Nigeria'), ('NI', 'Nicaragua'), ('NL', 'Netherlands'), ('NO', 'Norway'), ('NP', 'Nepal'), ('NR', 'Nauru'), ('NU', 'Niue'), ('NZ', 'New Zealand'), ('OM', 'Oman'), ('PA', 'Panama'), ('PE', 'Peru'), ('PF', 'French Polynesia'), ('PG', 'Papua New Guinea'), ('PH', 'Philippines'), ('PK', 'Pakistan'), ('PL', 'Poland'), ('PM', 'St. Pierre & Miquelon'), ('PN', 'Pitcairn'), ('PR', 'Puerto Rico'), ('PT', 'Portugal'), ('PW', 'Palau'), ('PY', 'Paraguay'), ('QA', 'Qatar'), ('RE', 'Reunion'), ('RO', 'Romania'), ('RU', 'Russian Federation'), ('RW', 'Rwanda'), ('SA', 'Saudi Arabia'), ('SB', 'Solomon Islands'), ('SC', 'Seychelles'), ('SD', 'Sudan'), ('SE', 'Sweden'), ('SG', 'Singapore'), ('SH', 'St. Helena'), ('SI', 'Slovenia'), ('SJ', 'Svalbard & Jan Mayen Islands'), ('SK', 'Slovakia'), ('SL', 'Sierra Leone'), ('SM', 'San Marino'), ('SN', 'Senegal'), ('SO', 'Somalia'), ('SR', 'Suriname'), ('ST', 'Sao Tome & Principe'), ('SV', 'El Salvador'), ('SY', 'Syrian Arab Republic'), ('SZ', 'Swaziland'), ('TC', 'Turks & Caicos Islands'), ('TD', 'Chad'), ('TF', 'French Southern Territories'), ('TG', 'Togo'), ('TH', 'Thailand'), ('TJ', 'Tajikistan'), ('TK', 'Tokelau'), ('TM', 'Turkmenistan'), ('TN', 'Tunisia'), ('TO', 'Tonga'), ('TP', 'East Timor'), ('TR', 'Turkey'), ('TT', 'Trinidad & Tobago'), ('TV', 'Tuvalu'), ('TW', 'Taiwan, Province of China'), ('TZ', 'Tanzania, United Republic of'), ('UA', 'Ukraine'), ('UG', 'Uganda'), ('UM', 'United States Minor Outlying Islands'), ('US', 'United States of America'), ('UY', 'Uruguay'), ('UZ', 'Uzbekistan'), ('VA', 'Vatican City State (Holy See)'), ('VC', 'St. Vincent & the Grenadines'), ('VE', 'Venezuela'), ('VG', 'British Virgin Islands'), ('VI', 'United States Virgin Islands'), ('VN', 'Viet Nam'), ('VU', 'Vanuatu'), ('WF', 'Wallis & Futuna Islands'), ('WS', 'Samoa'), ('YE', 'Yemen'), ('YT', 'Mayotte'), ('YU', 'Yugoslavia'), ('ZA', 'South Africa'), ('ZM', 'Zambia'), ('ZR', 'Zaire'), ('ZW', 'Zimbabwe')], default='CH', max_length=2), + ), + migrations.AlterField( + model_name='userbillingaddress', + name='country', + field=utils.fields.CountryField(choices=[('AD', 'Andorra'), ('AE', 'United Arab Emirates'), ('AF', 'Afghanistan'), ('AG', 'Antigua & Barbuda'), ('AI', 'Anguilla'), ('AL', 'Albania'), ('AM', 'Armenia'), ('AN', 'Netherlands Antilles'), ('AO', 'Angola'), ('AQ', 'Antarctica'), ('AR', 'Argentina'), ('AS', 'American Samoa'), ('AT', 'Austria'), ('AU', 'Australia'), ('AW', 'Aruba'), ('AZ', 'Azerbaijan'), ('BA', 'Bosnia and Herzegovina'), ('BB', 'Barbados'), ('BD', 'Bangladesh'), ('BE', 'Belgium'), ('BF', 'Burkina Faso'), ('BG', 'Bulgaria'), ('BH', 'Bahrain'), ('BI', 'Burundi'), ('BJ', 'Benin'), ('BM', 'Bermuda'), ('BN', 'Brunei Darussalam'), ('BO', 'Bolivia'), ('BR', 'Brazil'), ('BS', 'Bahama'), ('BT', 'Bhutan'), ('BV', 'Bouvet Island'), ('BW', 'Botswana'), ('BY', 'Belarus'), ('BZ', 'Belize'), ('CA', 'Canada'), ('CC', 'Cocos (Keeling) Islands'), ('CF', 'Central African Republic'), ('CG', 'Congo'), ('CH', 'Switzerland'), ('CI', 'Ivory Coast'), ('CK', 'Cook Iislands'), ('CL', 'Chile'), ('CM', 'Cameroon'), ('CN', 'China'), ('CO', 'Colombia'), ('CR', 'Costa Rica'), ('CU', 'Cuba'), ('CV', 'Cape Verde'), ('CX', 'Christmas Island'), ('CY', 'Cyprus'), ('CZ', 'Czech Republic'), ('DE', 'Germany'), ('DJ', 'Djibouti'), ('DK', 'Denmark'), ('DM', 'Dominica'), ('DO', 'Dominican Republic'), ('DZ', 'Algeria'), ('EC', 'Ecuador'), ('EE', 'Estonia'), ('EG', 'Egypt'), ('EH', 'Western Sahara'), ('ER', 'Eritrea'), ('ES', 'Spain'), ('ET', 'Ethiopia'), ('FI', 'Finland'), ('FJ', 'Fiji'), ('FK', 'Falkland Islands (Malvinas)'), ('FM', 'Micronesia'), ('FO', 'Faroe Islands'), ('FR', 'France'), ('FX', 'France, Metropolitan'), ('GA', 'Gabon'), ('GB', 'United Kingdom (Great Britain)'), ('GD', 'Grenada'), ('GE', 'Georgia'), ('GF', 'French Guiana'), ('GH', 'Ghana'), ('GI', 'Gibraltar'), ('GL', 'Greenland'), ('GM', 'Gambia'), ('GN', 'Guinea'), ('GP', 'Guadeloupe'), ('GQ', 'Equatorial Guinea'), ('GR', 'Greece'), ('GS', 'South Georgia and the South Sandwich Islands'), ('GT', 'Guatemala'), ('GU', 'Guam'), ('GW', 'Guinea-Bissau'), ('GY', 'Guyana'), ('HK', 'Hong Kong'), ('HM', 'Heard & McDonald Islands'), ('HN', 'Honduras'), ('HR', 'Croatia'), ('HT', 'Haiti'), ('HU', 'Hungary'), ('ID', 'Indonesia'), ('IE', 'Ireland'), ('IL', 'Israel'), ('IN', 'India'), ('IO', 'British Indian Ocean Territory'), ('IQ', 'Iraq'), ('IR', 'Islamic Republic of Iran'), ('IS', 'Iceland'), ('IT', 'Italy'), ('JM', 'Jamaica'), ('JO', 'Jordan'), ('JP', 'Japan'), ('KE', 'Kenya'), ('KG', 'Kyrgyzstan'), ('KH', 'Cambodia'), ('KI', 'Kiribati'), ('KM', 'Comoros'), ('KN', 'St. Kitts and Nevis'), ('KP', "Korea, Democratic People's Republic of"), ('KR', 'Korea, Republic of'), ('KW', 'Kuwait'), ('KY', 'Cayman Islands'), ('KZ', 'Kazakhstan'), ('LA', "Lao People's Democratic Republic"), ('LB', 'Lebanon'), ('LC', 'Saint Lucia'), ('LI', 'Liechtenstein'), ('LK', 'Sri Lanka'), ('LR', 'Liberia'), ('LS', 'Lesotho'), ('LT', 'Lithuania'), ('LU', 'Luxembourg'), ('LV', 'Latvia'), ('LY', 'Libyan Arab Jamahiriya'), ('MA', 'Morocco'), ('MC', 'Monaco'), ('MD', 'Moldova, Republic of'), ('MG', 'Madagascar'), ('MH', 'Marshall Islands'), ('ML', 'Mali'), ('MN', 'Mongolia'), ('MM', 'Myanmar'), ('MO', 'Macau'), ('MP', 'Northern Mariana Islands'), ('MQ', 'Martinique'), ('MR', 'Mauritania'), ('MS', 'Monserrat'), ('MT', 'Malta'), ('MU', 'Mauritius'), ('MV', 'Maldives'), ('MW', 'Malawi'), ('MX', 'Mexico'), ('MY', 'Malaysia'), ('MZ', 'Mozambique'), ('NA', 'Namibia'), ('NC', 'New Caledonia'), ('NE', 'Niger'), ('NF', 'Norfolk Island'), ('NG', 'Nigeria'), ('NI', 'Nicaragua'), ('NL', 'Netherlands'), ('NO', 'Norway'), ('NP', 'Nepal'), ('NR', 'Nauru'), ('NU', 'Niue'), ('NZ', 'New Zealand'), ('OM', 'Oman'), ('PA', 'Panama'), ('PE', 'Peru'), ('PF', 'French Polynesia'), ('PG', 'Papua New Guinea'), ('PH', 'Philippines'), ('PK', 'Pakistan'), ('PL', 'Poland'), ('PM', 'St. Pierre & Miquelon'), ('PN', 'Pitcairn'), ('PR', 'Puerto Rico'), ('PT', 'Portugal'), ('PW', 'Palau'), ('PY', 'Paraguay'), ('QA', 'Qatar'), ('RE', 'Reunion'), ('RO', 'Romania'), ('RU', 'Russian Federation'), ('RW', 'Rwanda'), ('SA', 'Saudi Arabia'), ('SB', 'Solomon Islands'), ('SC', 'Seychelles'), ('SD', 'Sudan'), ('SE', 'Sweden'), ('SG', 'Singapore'), ('SH', 'St. Helena'), ('SI', 'Slovenia'), ('SJ', 'Svalbard & Jan Mayen Islands'), ('SK', 'Slovakia'), ('SL', 'Sierra Leone'), ('SM', 'San Marino'), ('SN', 'Senegal'), ('SO', 'Somalia'), ('SR', 'Suriname'), ('ST', 'Sao Tome & Principe'), ('SV', 'El Salvador'), ('SY', 'Syrian Arab Republic'), ('SZ', 'Swaziland'), ('TC', 'Turks & Caicos Islands'), ('TD', 'Chad'), ('TF', 'French Southern Territories'), ('TG', 'Togo'), ('TH', 'Thailand'), ('TJ', 'Tajikistan'), ('TK', 'Tokelau'), ('TM', 'Turkmenistan'), ('TN', 'Tunisia'), ('TO', 'Tonga'), ('TP', 'East Timor'), ('TR', 'Turkey'), ('TT', 'Trinidad & Tobago'), ('TV', 'Tuvalu'), ('TW', 'Taiwan, Province of China'), ('TZ', 'Tanzania, United Republic of'), ('UA', 'Ukraine'), ('UG', 'Uganda'), ('UM', 'United States Minor Outlying Islands'), ('US', 'United States of America'), ('UY', 'Uruguay'), ('UZ', 'Uzbekistan'), ('VA', 'Vatican City State (Holy See)'), ('VC', 'St. Vincent & the Grenadines'), ('VE', 'Venezuela'), ('VG', 'British Virgin Islands'), ('VI', 'United States Virgin Islands'), ('VN', 'Viet Nam'), ('VU', 'Vanuatu'), ('WF', 'Wallis & Futuna Islands'), ('WS', 'Samoa'), ('YE', 'Yemen'), ('YT', 'Mayotte'), ('YU', 'Yugoslavia'), ('ZA', 'South Africa'), ('ZM', 'Zambia'), ('ZR', 'Zaire'), ('ZW', 'Zimbabwe')], default='CH', max_length=2), + ), + ] From 32cfdea68c9c744068d23247c75c68b820d05dc5 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 21 Dec 2019 10:05:58 +0530 Subject: [PATCH 400/795] Add missing vat_number field to user billing address --- utils/forms.py | 1 + utils/models.py | 15 +++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/utils/forms.py b/utils/forms.py index 71a675bc..6e8f4606 100644 --- a/utils/forms.py +++ b/utils/forms.py @@ -182,6 +182,7 @@ class UserBillingAddressForm(forms.ModelForm): 'city': _('City'), 'postal_code': _('Postal Code'), 'Country': _('Country'), + 'vat_number': _('VAT Number'), } diff --git a/utils/models.py b/utils/models.py index b6c0ef88..0084ddd4 100644 --- a/utils/models.py +++ b/utils/models.py @@ -38,10 +38,16 @@ class UserBillingAddress(BaseBillingAddress): current = models.BooleanField(default=True) def __str__(self): - return "%s, %s, %s, %s, %s" % ( - self.cardholder_name, self.street_address, self.city, - self.postal_code, self.country - ) + if self.vat_number: + return "%s, %s, %s, %s, %s, %s" % ( + self.cardholder_name, self.street_address, self.city, + self.postal_code, self.country, self.vat_number + ) + else: + return "%s, %s, %s, %s, %s" % ( + self.cardholder_name, self.street_address, self.city, + self.postal_code, self.country + ) def to_dict(self): return { @@ -50,6 +56,7 @@ class UserBillingAddress(BaseBillingAddress): 'City': self.city, 'Postal Code': self.postal_code, 'Country': self.country, + 'VAT Number': self.vat_number } From 8409acf02dfe06c3499a319888c368cf8ac37d3a Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 21 Dec 2019 10:10:32 +0530 Subject: [PATCH 401/795] Add missing vat_number field --- utils/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/forms.py b/utils/forms.py index 6e8f4606..f35c90f4 100644 --- a/utils/forms.py +++ b/utils/forms.py @@ -175,7 +175,7 @@ class UserBillingAddressForm(forms.ModelForm): class Meta: model = UserBillingAddress fields = ['cardholder_name', 'street_address', - 'city', 'postal_code', 'country', 'user'] + 'city', 'postal_code', 'country', 'user', 'vat_number'] labels = { 'cardholder_name': _('Cardholder Name'), 'street_address': _('Street Building'), From 3b6e5d448b62f89e65ab762d9b4d2ba959169690 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 24 Dec 2019 09:06:17 +0530 Subject: [PATCH 402/795] Add show non transparent navbar always option --- datacenterlight/cms_models.py | 5 +++++ ...odel_show_non_transparent_navbar_always.py | 20 +++++++++++++++++++ .../templates/datacenterlight/cms/navbar.html | 2 +- .../datacenterlight/includes/_navbar.html | 2 +- 4 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 datacenterlight/migrations/0030_dclnavbarpluginmodel_show_non_transparent_navbar_always.py diff --git a/datacenterlight/cms_models.py b/datacenterlight/cms_models.py index 2d1a98b5..d1c5c259 100644 --- a/datacenterlight/cms_models.py +++ b/datacenterlight/cms_models.py @@ -184,6 +184,11 @@ class DCLNavbarPluginModel(CMSPlugin): default=True, help_text='Uncheck this if you do not want to show login/dashboard.' ) + show_non_transparent_navbar_always = models.BooleanField( + default=False, + help_text='Check this if you want to show non transparent navbar only.' + '(Useful when we want to setup a simple page)' + ) def get_logo_dark(self): # used only if atleast one logo exists diff --git a/datacenterlight/migrations/0030_dclnavbarpluginmodel_show_non_transparent_navbar_always.py b/datacenterlight/migrations/0030_dclnavbarpluginmodel_show_non_transparent_navbar_always.py new file mode 100644 index 00000000..f3e3ec09 --- /dev/null +++ b/datacenterlight/migrations/0030_dclnavbarpluginmodel_show_non_transparent_navbar_always.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2019-12-24 03:34 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('datacenterlight', '0029_auto_20190420_1022'), + ] + + operations = [ + migrations.AddField( + model_name='dclnavbarpluginmodel', + name='show_non_transparent_navbar_always', + field=models.BooleanField(default=False, help_text='Check this if you want to show non transparent navbar only.(Useful when we want to setup a simple page)'), + ), + ] diff --git a/datacenterlight/templates/datacenterlight/cms/navbar.html b/datacenterlight/templates/datacenterlight/cms/navbar.html index 886a5009..8000ec54 100644 --- a/datacenterlight/templates/datacenterlight/cms/navbar.html +++ b/datacenterlight/templates/datacenterlight/cms/navbar.html @@ -1,7 +1,7 @@ {% load static i18n custom_tags cms_tags %} {% get_current_language as LANGUAGE_CODE %} -
+ + + + + """.format( + vm_id=vm_id if vm_id > 0 else "", + ip_addresses=mark_safe(get_ip_addresses(vm_id)) if vm_id > 0 else "", + period = mark_safe("%s — %s" % ( + datetime.datetime.fromtimestamp(start_date).strftime('%Y-%m-%d'), + datetime.datetime.fromtimestamp(end_date).strftime('%Y-%m-%d'))), + total=invoice.total/100, + stripe_invoice_url=invoice.hosted_invoice_url, + see_invoice_text=_("See Invoice") + )) + else: + return "" diff --git a/hosting/templates/hosting/dashboard.html b/hosting/templates/hosting/dashboard.html index bda6eb11..212c5885 100644 --- a/hosting/templates/hosting/dashboard.html +++ b/hosting/templates/hosting/dashboard.html @@ -29,7 +29,7 @@ - +

{% trans "My Bills" %}

diff --git a/hosting/templates/hosting/invoices.html b/hosting/templates/hosting/invoices.html index 5f7be4b4..f48802d1 100644 --- a/hosting/templates/hosting/invoices.html +++ b/hosting/templates/hosting/invoices.html @@ -26,36 +26,46 @@
- {% for invoice in invoices %} + {% for inv_data in invs %} - - - {% with period|get_value_from_dict:invoice.invoice_number as period_to_show %} - - {% endwith %} - - + {{ inv_data | get_line_item_from_stripe_invoice }} {% endfor %}
{% trans "Order Nr." %}{% trans "Invoice Nr." %} {% trans "Date" %} {% trans "Amount" %}
{{ order.id }}{{ order.created_at | date:'Y-m-d h:i a' }}{{ order.price|floatformat:2|intcomma }}{{ invoice.invoice_number }}{{ invoice.paid_at | date:'Y-m-d h:i a' }}{{ invoice.total_in_chf|floatformat:2|intcomma }} - {% trans 'See Invoice' %} + {% trans 'See Invoice' %}
{{ invoice.paid_at | date:'Y-m-d h:i a' }} {{ invoice.total_in_chf|floatformat:2|intcomma }} - {% trans 'See Invoice' %} + {% trans 'See Invoice' %}
{vm_id}{ip_addresses}{period}{total} + {see_invoice_text} +
{{ invoice.order.vm_id }}{{ ips|get_value_from_dict:invoice.invoice_number|join:"
" }}
{{ period_to_show.period_start | date:'Y-m-d' }} — {{ period_to_show.period_end | date:'Y-m-d' }}{{ invoice.total_in_chf|floatformat:2|intcomma }} - {% trans 'See Invoice' %} -
- - {% if is_paginated %} - +{% if invs.has_other_pages %} +
    + {% if invs.has_previous %} + {% if user_email %} +
  • «
  • + {% else %} +
  • «
  • + {% endif %} + {% else %} +
  • «
  • {% endif %} + {% for i in invs.paginator.page_range %} + {% if invs.number == i %} +
  • {{ i }} (current)
  • + {% else %} + {% if user_email %} +
  • {{ i }}
  • + {% else %} +
  • {{ i }}
  • + {% endif %} + {% endif %} + {% endfor %} + {% if invs.has_next %} + {% if user_email %} +
  • »
  • + {% else %} +
  • »
  • + {% endif %} + {% else %} +
  • »
  • + {% endif %} +
+{% endif %} + {% endblock %} diff --git a/hosting/views.py b/hosting/views.py index bd72af94..c5d505bf 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -3,6 +3,7 @@ import uuid from datetime import datetime from time import sleep +import stripe from django import forms from django.conf import settings from django.contrib import messages @@ -10,6 +11,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.tokens import default_token_generator from django.core.exceptions import ValidationError from django.core.files.base import ContentFile +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.core.urlresolvers import reverse_lazy, reverse from django.http import ( Http404, HttpResponseRedirect, HttpResponse, JsonResponse @@ -40,7 +42,6 @@ from datacenterlight.models import VMTemplate, VMPricing from datacenterlight.utils import ( create_vm, get_cms_integration, check_otp, validate_vat_number ) -from dynamicweb.settings.base import DCL_ERROR_EMAILS_TO_LIST from hosting.models import UserCardDetail from membership.models import CustomUser, StripeCustomer from opennebula_api.models import OpenNebulaManager @@ -57,11 +58,10 @@ from utils.hosting_utils import ( get_vm_price_with_vat, get_vm_price_for_given_vat, HostingUtils, get_vat_rate_for_country ) +from utils.ldap_manager import LdapManager from utils.mailer import BaseEmail from utils.stripe_utils import StripeUtils from utils.tasks import send_plain_email_task -from utils.ldap_manager import LdapManager - from utils.views import ( PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin, ResendActivationLinkViewMixin @@ -73,7 +73,7 @@ from .forms import ( from .mixins import ProcessVMSelectionMixin, HostingContextMixin from .models import ( HostingOrder, HostingBill, HostingPlan, UserHostingKey, VMDetail, - GenericProduct, MonthlyHostingBill, HostingBillLineItem + GenericProduct, MonthlyHostingBill ) logger = logging.getLogger(__name__) @@ -1240,7 +1240,7 @@ class OrdersHostingListView(LoginRequiredMixin, ListView): return super(OrdersHostingListView, self).get(request, *args, **kwargs) -class InvoiceListView(LoginRequiredMixin, ListView): +class InvoiceListView(LoginRequiredMixin, TemplateView): template_name = "hosting/invoices.html" login_url = reverse_lazy('hosting:login') context_object_name = "invoices" @@ -1248,10 +1248,13 @@ class InvoiceListView(LoginRequiredMixin, ListView): ordering = '-created' def get_context_data(self, **kwargs): + page = self.request.GET.get('page', 1) context = super(InvoiceListView, self).get_context_data(**kwargs) + invs_page = None if ('user_email' in self.request.GET and self.request.user.email == settings.ADMIN_EMAIL): user_email = self.request.GET['user_email'] + context['user_email'] = user_email logger.debug( "user_email = {}".format(user_email) ) @@ -1260,54 +1263,34 @@ class InvoiceListView(LoginRequiredMixin, ListView): except CustomUser.DoesNotExist as dne: logger.debug("User does not exist") cu = self.request.user - mhbs = MonthlyHostingBill.objects.filter(customer__user=cu) - else: - mhbs = MonthlyHostingBill.objects.filter( - customer__user=self.request.user - ) - ips_dict = {} - line_item_period_dict = {} - for mhb in mhbs: + invs = stripe.Invoice.list(customer=cu.stripecustomer.stripe_id, + count=100) + paginator = Paginator(invs.data, 10) try: - vm_detail = VMDetail.objects.get(vm_id=mhb.order.vm_id) - ips_dict[mhb.invoice_number] = [vm_detail.ipv6, vm_detail.ipv4] - all_line_items = HostingBillLineItem.objects.filter(monthly_hosting_bill=mhb) - for line_item in all_line_items: - if line_item.get_item_detail_str() != "": - line_item_period_dict[mhb.invoice_number] = { - "period_start": line_item.period_start, - "period_end": line_item.period_end - } - break - except VMDetail.DoesNotExist as dne: - ips_dict[mhb.invoice_number] = ['--'] - logger.debug("VMDetail for {} doesn't exist".format( - mhb.order.vm_id - )) - context['ips'] = ips_dict - context['period'] = line_item_period_dict + invs_page = paginator.page(page) + except PageNotAnInteger: + invs_page = paginator.page(1) + except EmptyPage: + invs_page = paginator.page(paginator.num_pages) + else: + try: + invs = stripe.Invoice.list( + customer=self.request.user.stripecustomer.stripe_id, + count=100 + ) + paginator = Paginator(invs.data, 10) + try: + invs_page = paginator.page(page) + except PageNotAnInteger: + invs_page = paginator.page(1) + except EmptyPage: + invs_page = paginator.page(paginator.num_pages) + except Exception as ex: + logger.error(str(ex)) + invs_page = None + context["invs"] = invs_page return context - def get_queryset(self): - user = self.request.user - if ('user_email' in self.request.GET - and self.request.user.email == settings.ADMIN_EMAIL): - user_email = self.request.GET['user_email'] - logger.debug( - "user_email = {}".format(user_email) - ) - try: - cu = CustomUser.objects.get(email=user_email) - except CustomUser.DoesNotExist as dne: - logger.debug("User does not exist") - cu = self.request.user - self.queryset = MonthlyHostingBill.objects.filter(customer__user=cu) - else: - self.queryset = MonthlyHostingBill.objects.filter( - customer__user=self.request.user - ) - return super(InvoiceListView, self).get_queryset() - @method_decorator(decorators) def get(self, request, *args, **kwargs): return super(InvoiceListView, self).get(request, *args, **kwargs) diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index 73e2c035..3674185c 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -199,6 +199,15 @@ def get_vat_rate_for_country(country): return 0 +def get_ip_addresses(vm_id): + try: + vm_detail = VMDetail.objects.get(vm_id=vm_id) + return "%s
%s" % (vm_detail.ipv6, vm_detail.ipv4) + except VMDetail.DoesNotExist as dne: + logger.error(str(dne)) + logger.error("VMDetail for %s does not exist" % vm_id) + return "--" + class HostingUtils: @staticmethod def clear_items_from_list(from_list, items_list): From 0b2a305f57ece9368eabfdc036ca0a438e68f280 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 20 Jan 2020 12:09:49 +0530 Subject: [PATCH 515/795] Fix pep warning --- utils/hosting_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index 3674185c..f47905a5 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -208,6 +208,7 @@ def get_ip_addresses(vm_id): logger.error("VMDetail for %s does not exist" % vm_id) return "--" + class HostingUtils: @staticmethod def clear_items_from_list(from_list, items_list): From a82c4d556c75097c1d1da1017660acff0d47a208 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 20 Jan 2020 13:25:49 +0530 Subject: [PATCH 516/795] Update Changelog for 2.9.5 --- Changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog b/Changelog index 5c3b0551..566f5960 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,5 @@ +2.9.5: 2020-01-20 + * Feature: Show invoices directly from stripe (MR!727) 2.9.4: 2020-01-10 * Bugfix: Buying VPN generic item caused 500 error 2.9.3: 2020-01-05 From 399f9ed6c9c34b93f2af7feb44dbc995b448dec9 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 23 Jan 2020 16:37:35 +0530 Subject: [PATCH 517/795] Adjust hosting VM buy flow --- hosting/views.py | 38 ++++++++++++++++++++++++++++++++++---- utils/stripe_utils.py | 32 ++++++++++++++++++++++---------- 2 files changed, 56 insertions(+), 14 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index c5d505bf..43b6f5a7 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -42,7 +42,7 @@ from datacenterlight.models import VMTemplate, VMPricing from datacenterlight.utils import ( create_vm, get_cms_integration, check_otp, validate_vat_number ) -from hosting.models import UserCardDetail +from hosting.models import UserCardDetail, StripeTaxRate from membership.models import CustomUser, StripeCustomer from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import ( @@ -1135,7 +1135,8 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView): cpu = specs.get('cpu') memory = specs.get('memory') disk_size = specs.get('disk_size') - amount_to_be_charged = specs.get('total_price') + amount_to_be_charged = specs.get('price') + discount = specs.get('discount') plan_name = StripeUtils.get_stripe_plan_name( cpu=cpu, memory=memory, @@ -1154,10 +1155,39 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView): amount=amount_to_be_charged, name=plan_name, stripe_plan_id=stripe_plan_id) + # Create StripeTaxRate if applicable to the user + stripe_tax_rate = None + if specs["vat_percent"] > 0: + try: + stripe_tax_rate = StripeTaxRate.objects.get( + description="VAT for %s" % specs["vat_country"] + ) + print("Stripe Tax Rate exists") + except StripeTaxRate.DoesNotExist as dne: + print("StripeTaxRate does not exist") + tax_rate_obj = stripe.TaxRate.create( + display_name="VAT", + description="VAT for %s" % specs["vat_country"], + jurisdiction=specs["vat_country"], + percentage=specs["vat_percent"] * 100, + inclusive=False, + ) + stripe_tax_rate = StripeTaxRate.objects.create( + display_name=tax_rate_obj.display_name, + description=tax_rate_obj.description, + jurisdiction=tax_rate_obj.jurisdiction, + percentage=tax_rate_obj.percentage, + inclusive=False, + tax_rate_id=tax_rate_obj.id + ) + logger.debug("Created StripeTaxRate %s" % + stripe_tax_rate.tax_rate_id) subscription_result = stripe_utils.subscribe_customer_to_plan( stripe_api_cus_id, - [{"plan": stripe_plan.get( - 'response_object').stripe_plan_id}]) + [{"plan": stripe_plan.get('response_object').stripe_plan_id}], + coupon='ipv6-discount-8chf' if 'name' in discount and 'ipv6' in discount['name'].lower() else "", + tax_rate=[stripe_tax_rate.tax_rate_id] if stripe_tax_rate else [], + ) stripe_subscription_obj = subscription_result.get('response_object') # Check if the subscription was approved and is active if (stripe_subscription_obj is None or diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py index de16fe4b..45904f14 100644 --- a/utils/stripe_utils.py +++ b/utils/stripe_utils.py @@ -297,7 +297,8 @@ class StripeUtils(object): return return_value @handleStripeError - def subscribe_customer_to_plan(self, customer, plans, trial_end=None): + def subscribe_customer_to_plan(self, customer, plans, trial_end=None, + coupon="", tax_rates=list()): """ Subscribes the given customer to the list of given plans @@ -316,7 +317,9 @@ class StripeUtils(object): """ subscription_result = self.stripe.Subscription.create( - customer=customer, items=plans, trial_end=trial_end + customer=customer, items=plans, trial_end=trial_end, + coupon=coupon, + default_tax_rates=tax_rates, ) return subscription_result @@ -410,18 +413,27 @@ class StripeUtils(object): @staticmethod - def get_stripe_plan_name(cpu, memory, disk_size, price): + def get_stripe_plan_name(cpu, memory, disk_size, price, excl_vat=True): """ Returns the Stripe plan name :return: """ - return "{cpu} Cores, {memory} GB RAM, {disk_size} GB SSD, " \ - "{price} CHF".format( - cpu=cpu, - memory=memory, - disk_size=disk_size, - price=round(price, 2) - ) + if excl_vat: + return "{cpu} Cores, {memory} GB RAM, {disk_size} GB SSD, " \ + "{price} CHF Excl. VAT".format( + cpu=cpu, + memory=memory, + disk_size=disk_size, + price=round(price, 2) + ) + else: + return "{cpu} Cores, {memory} GB RAM, {disk_size} GB SSD, " \ + "{price} CHF".format( + cpu=cpu, + memory=memory, + disk_size=disk_size, + price=round(price, 2) + ) @handleStripeError def set_subscription_meta_data(self, subscription_id, meta_data): From ec00785068dc38f9b946cc0e1f371b3f298a96b5 Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 24 Jan 2020 14:10:56 +0530 Subject: [PATCH 518/795] Update get_stripe_plan_id to make it compatible with excl_vat --- utils/stripe_utils.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py index 45904f14..ade06dd3 100644 --- a/utils/stripe_utils.py +++ b/utils/stripe_utils.py @@ -1,7 +1,9 @@ import logging import re + import stripe from django.conf import settings + from datacenterlight.models import StripePlan stripe.api_key = settings.STRIPE_API_PRIVATE_KEY @@ -351,7 +353,7 @@ class StripeUtils(object): @staticmethod def get_stripe_plan_id(cpu, ram, ssd, version, app='dcl', hdd=None, - price=None): + price=None, excl_vat=True): """ Returns the Stripe plan id string of the form `dcl-v1-cpu-2-ram-5gb-ssd-10gb` based on the input parameters @@ -378,13 +380,16 @@ class StripeUtils(object): plan=dcl_plan_string ) if price is not None: - stripe_plan_id_string_with_price = '{}-{}chf'.format( + stripe_plan_id_string = '{}-{}chf'.format( stripe_plan_id_string, round(price, 2) ) - return stripe_plan_id_string_with_price - else: - return stripe_plan_id_string + if excl_vat: + stripe_plan_id_string = '{}-{}'.format( + stripe_plan_id_string, + "excl_vat" + ) + return stripe_plan_id_string @staticmethod def get_vm_config_from_stripe_id(stripe_id): From e01b27835ea64f2001834fde56c46ba8726d065d Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 24 Jan 2020 14:11:55 +0530 Subject: [PATCH 519/795] Make subscription exclusive of VAT + Add VAT separately to subscription --- datacenterlight/views.py | 58 +++++++++++++++++++++++++++++++++++----- hosting/views.py | 2 +- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index d2581fd9..92c65681 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -1,5 +1,6 @@ import logging +import stripe from django import forms from django.conf import settings from django.contrib import messages @@ -17,8 +18,8 @@ from hosting.forms import ( UserHostingKeyForm ) from hosting.models import ( - HostingBill, HostingOrder, UserCardDetail, GenericProduct, UserHostingKey -) + HostingBill, HostingOrder, UserCardDetail, GenericProduct, UserHostingKey, + StripeTaxRate) from membership.models import CustomUser, StripeCustomer from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import VMTemplateSerializer @@ -840,9 +841,15 @@ class OrderConfirmationView(DetailView, FormView): (request.session['generic_payment_details']['recurring'])): recurring_interval = 'month' if 'generic_payment_details' in request.session: + vat_percent = request.session['generic_payment_details']['vat_rate'] + vat_country = request.session['generic_payment_details']['vat_country'] + if 'discount' in request.session['generic_payment_details']: + discount = request.session['generic_payment_details']['discount'] + else: + discount = {'name': '', 'amount': 0, 'coupon_id': ''} amount_to_be_charged = ( round( - request.session['generic_payment_details']['amount'], + request.session['generic_payment_details']['amount_before_vat'], 2 ) ) @@ -863,7 +870,10 @@ class OrderConfirmationView(DetailView, FormView): cpu = specs.get('cpu') memory = specs.get('memory') disk_size = specs.get('disk_size') - amount_to_be_charged = specs.get('total_price') + amount_to_be_charged = specs.get('price') + vat_percent = specs.get('vat_percent') + vat_country = specs.get('vat_country') + discount = specs.get('discount') plan_name = StripeUtils.get_stripe_plan_name( cpu=cpu, memory=memory, @@ -884,10 +894,46 @@ class OrderConfirmationView(DetailView, FormView): stripe_plan_id=stripe_plan_id, interval=recurring_interval ) + # Create StripeTaxRate if applicable to the user + logger.debug("vat_percent = %s, vat_country = %s" % + (vat_percent, vat_country) + ) + stripe_tax_rate = None + if vat_percent > 0: + try: + stripe_tax_rate = StripeTaxRate.objects.get( + description="VAT for %s" % vat_country + ) + print("Stripe Tax Rate exists") + except StripeTaxRate.DoesNotExist as dne: + print("StripeTaxRate does not exist") + tax_rate_obj = stripe.TaxRate.create( + display_name="VAT", + description="VAT for %s" % vat_country, + jurisdiction=vat_country, + percentage=vat_percent, + inclusive=False, + ) + stripe_tax_rate = StripeTaxRate.objects.create( + display_name=tax_rate_obj.display_name, + description=tax_rate_obj.description, + jurisdiction=tax_rate_obj.jurisdiction, + percentage=tax_rate_obj.percentage, + inclusive=False, + tax_rate_id=tax_rate_obj.id + ) + logger.debug("Created StripeTaxRate %s" % + stripe_tax_rate.tax_rate_id) subscription_result = stripe_utils.subscribe_customer_to_plan( stripe_api_cus_id, - [{"plan": stripe_plan.get( - 'response_object').stripe_plan_id}]) + [{"plan": stripe_plan.get('response_object').stripe_plan_id}], + coupon='ipv6-discount-8chf' if ( + 'name' in discount and + discount['name'] is not None and + 'ipv6' in discount['name'].lower() + ) else "", + tax_rates=[stripe_tax_rate.tax_rate_id] if stripe_tax_rate else [], + ) stripe_subscription_obj = subscription_result.get('response_object') # Check if the subscription was approved and is active if (stripe_subscription_obj is None diff --git a/hosting/views.py b/hosting/views.py index 43b6f5a7..672868d8 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1186,7 +1186,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView): stripe_api_cus_id, [{"plan": stripe_plan.get('response_object').stripe_plan_id}], coupon='ipv6-discount-8chf' if 'name' in discount and 'ipv6' in discount['name'].lower() else "", - tax_rate=[stripe_tax_rate.tax_rate_id] if stripe_tax_rate else [], + tax_rates=[stripe_tax_rate.tax_rate_id] if stripe_tax_rate else [], ) stripe_subscription_obj = subscription_result.get('response_object') # Check if the subscription was approved and is active From 38550ea75c5de1b3de12a6979813793a1900bedb Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 24 Jan 2020 15:00:16 +0530 Subject: [PATCH 520/795] Rearrange the way we show VAT and discount calculations --- .../datacenterlight/order_detail.html | 20 +++++++++++-------- utils/hosting_utils.py | 8 +++++--- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index db3f73a8..80e35914 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -126,21 +126,25 @@
{% if vm.vat > 0 %}

- {% trans "Subtotal" %} + {% trans "Price" %} {{vm.price|floatformat:2|intcomma}} CHF

+ {% if vm.discount.amount > 0 %} +

+ {%trans "Discount" as discount_name %} + {{ vm.discount.name|default:discount_name }} + - {{ vm.discount.amount }} CHF +

+ {% endif %} +

+ {% trans "Subtotal" %} + {{vm.price - vm.discount.amount|floatformat:2|intcomma}} CHF +

{% trans "VAT for" %} {{vm.vat_country}} ({{vm.vat_percent}}%) : {{vm.vat|floatformat:2|intcomma}} CHF

{% endif %} - {% if vm.discount.amount > 0 %} -

- {%trans "Discount" as discount_name %} - {{ vm.discount.name|default:discount_name }} - - {{ vm.discount.amount }} CHF -

- {% endif %}
diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index f47905a5..2705143c 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -104,15 +104,17 @@ def get_vm_price_for_given_vat(cpu, memory, ssd_size, hdd_size=0, (decimal.Decimal(hdd_size) * pricing.hdd_unit_price) ) - vat = price * decimal.Decimal(vat_rate) * decimal.Decimal(0.01) + discount_name = pricing.discount_name + discount_amount = round(float(pricing.discount_amount), 2) + vat = (price - discount_amount) * decimal.Decimal(vat_rate) * decimal.Decimal(0.01) vat_percent = vat_rate cents = decimal.Decimal('.01') price = price.quantize(cents, decimal.ROUND_HALF_UP) vat = vat.quantize(cents, decimal.ROUND_HALF_UP) discount = { - 'name': pricing.discount_name, - 'amount': round(float(pricing.discount_amount), 2) + 'name': discount_name, + 'amount': discount_amount } return (round(float(price), 2), round(float(vat), 2), round(float(vat_percent), 2), discount) From 112f3e17a9ce7d2f998546e7690261c2a000225b Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 24 Jan 2020 15:03:07 +0530 Subject: [PATCH 521/795] Convert to decimal for type consistency --- utils/hosting_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index 2705143c..3312474d 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -106,7 +106,7 @@ def get_vm_price_for_given_vat(cpu, memory, ssd_size, hdd_size=0, discount_name = pricing.discount_name discount_amount = round(float(pricing.discount_amount), 2) - vat = (price - discount_amount) * decimal.Decimal(vat_rate) * decimal.Decimal(0.01) + vat = (price - decimal.Decimal(discount_amount)) * decimal.Decimal(vat_rate) * decimal.Decimal(0.01) vat_percent = vat_rate cents = decimal.Decimal('.01') From 156930ab26418f260967cf5cf60f250cb27f81ba Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 24 Jan 2020 15:24:46 +0530 Subject: [PATCH 522/795] Add price_after_discount explicitly for showing in template --- datacenterlight/templates/datacenterlight/order_detail.html | 2 +- datacenterlight/views.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 80e35914..c2b97556 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -138,7 +138,7 @@ {% endif %}

{% trans "Subtotal" %} - {{vm.price - vm.discount.amount|floatformat:2|intcomma}} CHF + {{vm.price_after_discount}} CHF

{% trans "VAT for" %} {{vm.vat_country}} ({{vm.vat_percent}}%) : diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 92c65681..8c1fe504 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -642,6 +642,7 @@ class OrderConfirmationView(DetailView, FormView): vat_rate=user_country_vat_rate * 100 ) vm_specs["price"] = price + vm_specs["price_after_discount"] = price - discount vat_number = request.session.get('billing_address_data').get("vat_number") billing_address = BillingAddress.objects.get(id=request.session["billing_address_id"]) From d1fd57b7308b60ab8e82ba56bfcd44823ce0d724 Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 24 Jan 2020 15:26:41 +0530 Subject: [PATCH 523/795] Correct the way we get amount from discount --- datacenterlight/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 8c1fe504..b2dec576 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -642,7 +642,7 @@ class OrderConfirmationView(DetailView, FormView): vat_rate=user_country_vat_rate * 100 ) vm_specs["price"] = price - vm_specs["price_after_discount"] = price - discount + vm_specs["price_after_discount"] = price - discount["amount"] vat_number = request.session.get('billing_address_data').get("vat_number") billing_address = BillingAddress.objects.get(id=request.session["billing_address_id"]) From cde6c51d4b22a3b8ada0a4362c8d17daff0847d7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 26 Jan 2020 10:06:31 +0530 Subject: [PATCH 524/795] Reset vat_validation_status when on payment page --- datacenterlight/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index b2dec576..0d62cf1e 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -309,6 +309,7 @@ class PaymentOrderView(FormView): @cache_control(no_cache=True, must_revalidate=True, no_store=True) def get(self, request, *args, **kwargs): + request.session.pop('vat_validation_status') if (('type' in request.GET and request.GET['type'] == 'generic') or 'product_slug' in kwargs): request.session['generic_payment_type'] = 'generic' From 24740438f7f05eff23561454928a3eb06faa3044 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 29 Jan 2020 16:02:26 +0530 Subject: [PATCH 525/795] get_vm_price_for_given_vat: Also return discount amount with vat --- utils/hosting_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index 3312474d..61f849ad 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -114,7 +114,8 @@ def get_vm_price_for_given_vat(cpu, memory, ssd_size, hdd_size=0, vat = vat.quantize(cents, decimal.ROUND_HALF_UP) discount = { 'name': discount_name, - 'amount': discount_amount + 'amount': discount_amount, + 'amount_with_vat': round(discount_amount * vat_rate, 2) } return (round(float(price), 2), round(float(vat), 2), round(float(vat_percent), 2), discount) From 970834cc38e77a2d3e3a1c9275e5cb677a157fc3 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 29 Jan 2020 16:03:13 +0530 Subject: [PATCH 526/795] Add parameters needed for displaying prices after discount, and vat --- datacenterlight/views.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 0d62cf1e..f1911cbf 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -681,6 +681,9 @@ class OrderConfirmationView(DetailView, FormView): 2) vm_specs["vat_country"] = user_vat_country vm_specs["discount"] = discount + vm_specs["price_with_vat"] = price + vat + vm_specs["price_after_discount"] = round(price - discount['amount'], 2) + vm_specs["price_after_discount_with_vat"] = round((price - discount['amount']) + vm_specs["vat"], 2) request.session['specs'] = vm_specs context.update({ From b8eca59e0dbfdae39daf2b6f0420735eda832116 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 29 Jan 2020 16:04:03 +0530 Subject: [PATCH 527/795] Fix design for showing prices excl and incl vat and discount --- .../datacenterlight/order_detail.html | 127 +++++++++++++----- 1 file changed, 94 insertions(+), 33 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index c2b97556..f62fd063 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -121,40 +121,101 @@


- {% if vm.vat > 0 or vm.discount.amount > 0 %} -
-
- {% if vm.vat > 0 %} -

- {% trans "Price" %} - {{vm.price|floatformat:2|intcomma}} CHF -

- {% if vm.discount.amount > 0 %} -

- {%trans "Discount" as discount_name %} - {{ vm.discount.name|default:discount_name }} - - {{ vm.discount.amount }} CHF -

- {% endif %} -

- {% trans "Subtotal" %} - {{vm.price_after_discount}} CHF -

-

- {% trans "VAT for" %} {{vm.vat_country}} ({{vm.vat_percent}}%) : - {{vm.vat|floatformat:2|intcomma}} CHF -

- {% endif %} -
-
-
-
-
- {% endif %}
-

- {% trans "Total" %} - {{vm.total_price|floatformat:2|intcomma}} CHF +

+ {% trans "Price Before VAT" %} + {{vm.price|intcomma}} CHF +

+
+
+
+
+
+ +
+
+

+
+
+

Pre VAT

+
+
+

VAT for {{vm.vat_country}} ({{vm.vat_percent}}%)

+
+
+
+
+

Price

+
+
+

{{vm.price|intcomma}} CHF

+
+
+

{{vm.price_with_vat|intcomma}} CHF

+
+
+ {% if vm.discount.amount > 0 %} +
+
+

{{vm.discount.name}}

+
+
+

-{{vm.discount.amount|intcomma}} CHF

+
+
+

-{{vm.discount.amount_with_vat|intcomma}} CHF

+
+
+ {% endif %} +
+
+
+
+
+
+
+

Total

+
+
+

{{vm.price_after_discount|intcomma}} CHF

+
+
+

{{vm.price_after_discount_with_vat|intcomma}} CHF

+
+
+
+
+
+
+
+

+ {% trans "Your Price in Total" %} + {{vm.total_price|floatformat:2|intcomma}} CHF

From 1f79ccd490ac9734d7f40508ec88da2effb545a9 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 29 Jan 2020 16:15:28 +0530 Subject: [PATCH 528/795] Convert discount amount into CHF --- utils/hosting_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index 61f849ad..85829d05 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -115,7 +115,7 @@ def get_vm_price_for_given_vat(cpu, memory, ssd_size, hdd_size=0, discount = { 'name': discount_name, 'amount': discount_amount, - 'amount_with_vat': round(discount_amount * vat_rate, 2) + 'amount_with_vat': round(discount_amount * vat_rate * 0.01, 2) } return (round(float(price), 2), round(float(vat), 2), round(float(vat_percent), 2), discount) From 48cc2b49394c97433ed2d435490b38edc908b1f2 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 29 Jan 2020 16:19:48 +0530 Subject: [PATCH 529/795] Fix discount amount calculation after VAT --- utils/hosting_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index 85829d05..e20e9b16 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -115,7 +115,7 @@ def get_vm_price_for_given_vat(cpu, memory, ssd_size, hdd_size=0, discount = { 'name': discount_name, 'amount': discount_amount, - 'amount_with_vat': round(discount_amount * vat_rate * 0.01, 2) + 'amount_with_vat': round(discount_amount * (1 + vat_rate * 0.01), 2) } return (round(float(price), 2), round(float(vat), 2), round(float(vat_percent), 2), discount) From 6132638faa2e438196ce12f20a6f2fbdfa563da2 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 29 Jan 2020 16:32:07 +0530 Subject: [PATCH 530/795] Use correct vat --- datacenterlight/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index f1911cbf..f8f7f928 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -681,7 +681,7 @@ class OrderConfirmationView(DetailView, FormView): 2) vm_specs["vat_country"] = user_vat_country vm_specs["discount"] = discount - vm_specs["price_with_vat"] = price + vat + vm_specs["price_with_vat"] = price + vm_specs["vat"] vm_specs["price_after_discount"] = round(price - discount['amount'], 2) vm_specs["price_after_discount_with_vat"] = round((price - discount['amount']) + vm_specs["vat"], 2) request.session['specs'] = vm_specs From a81fdc8ec1a344f92a665a795e5088237b9781f9 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 29 Jan 2020 16:45:07 +0530 Subject: [PATCH 531/795] Revert back to original vat calculation --- utils/hosting_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index e20e9b16..da2540d6 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -106,7 +106,7 @@ def get_vm_price_for_given_vat(cpu, memory, ssd_size, hdd_size=0, discount_name = pricing.discount_name discount_amount = round(float(pricing.discount_amount), 2) - vat = (price - decimal.Decimal(discount_amount)) * decimal.Decimal(vat_rate) * decimal.Decimal(0.01) + vat = price * decimal.Decimal(vat_rate) * decimal.Decimal(0.01) vat_percent = vat_rate cents = decimal.Decimal('.01') From ad606c2c554a854e1499b7ec86b04d851f956e91 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 29 Jan 2020 16:50:05 +0530 Subject: [PATCH 532/795] Correct calculation of total price --- datacenterlight/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index f8f7f928..6dec71d2 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -677,13 +677,13 @@ class OrderConfirmationView(DetailView, FormView): vm_specs["vat"] = vat vm_specs["vat_percent"] = vat_percent vm_specs["vat_validation_status"] = request.session["vat_validation_status"] if "vat_validation_status" in request.session else "" - vm_specs["total_price"] = round(price + vm_specs["vat"] - discount['amount'], + vm_specs["total_price"] = round(price + vm_specs["vat"] - discount['amount_with_vat'], 2) vm_specs["vat_country"] = user_vat_country vm_specs["discount"] = discount vm_specs["price_with_vat"] = price + vm_specs["vat"] vm_specs["price_after_discount"] = round(price - discount['amount'], 2) - vm_specs["price_after_discount_with_vat"] = round((price - discount['amount']) + vm_specs["vat"], 2) + vm_specs["price_after_discount_with_vat"] = round((price - discount['amount_with_vat']) + vm_specs["vat"], 2) request.session['specs'] = vm_specs context.update({ From 8ee4081f6011af7c4506c42ec7c3b5b41c611a1e Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 29 Jan 2020 16:58:47 +0530 Subject: [PATCH 533/795] Fix round up calculations --- utils/hosting_utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index da2540d6..5d898b0a 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -112,10 +112,12 @@ def get_vm_price_for_given_vat(cpu, memory, ssd_size, hdd_size=0, cents = decimal.Decimal('.01') price = price.quantize(cents, decimal.ROUND_HALF_UP) vat = vat.quantize(cents, decimal.ROUND_HALF_UP) + discount_amount_with_vat = decimal.Decimal(discount_amount) * (1 + decimal.Decimal(vat_rate) * decimal.Decimal(0.01)) + discount_amount_with_vat = discount_amount_with_vat.quantize(cents, decimal.ROUND_HALF_UP) discount = { 'name': discount_name, 'amount': discount_amount, - 'amount_with_vat': round(discount_amount * (1 + vat_rate * 0.01), 2) + 'amount_with_vat': round(float(discount_amount_with_vat), 2) } return (round(float(price), 2), round(float(vat), 2), round(float(vat_percent), 2), discount) From 4128aeb64da8a964e9d0794fc0b9d095740fdd00 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 29 Jan 2020 17:21:59 +0530 Subject: [PATCH 534/795] Add line height for final price --- datacenterlight/static/datacenterlight/css/hosting.css | 1 + 1 file changed, 1 insertion(+) diff --git a/datacenterlight/static/datacenterlight/css/hosting.css b/datacenterlight/static/datacenterlight/css/hosting.css index 0f16ab77..bcf266cc 100644 --- a/datacenterlight/static/datacenterlight/css/hosting.css +++ b/datacenterlight/static/datacenterlight/css/hosting.css @@ -532,6 +532,7 @@ .order-detail-container .total-price { font-size: 18px; + line-height: 20px; } @media (max-width: 767px) { From f546c5cb4f032b7c071b50aee328c6b224cd27b8 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 29 Jan 2020 17:38:44 +0530 Subject: [PATCH 535/795] Increase content space in order detail desktop view --- .../templates/datacenterlight/order_detail.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index f62fd063..fbe6ef16 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -104,7 +104,7 @@ {{ request.session.template.name }}

-
+

{% trans "Cores" %}: {{vm.cpu|floatformat}} @@ -121,7 +121,7 @@


-
+

{% trans "Price Before VAT" %} {{vm.price|intcomma}} CHF @@ -130,7 +130,7 @@


-
+
-
+

{% trans "Cores" %}: {{vm.cpu|floatformat}} @@ -121,7 +132,7 @@


-
+

{% trans "Price Before VAT" %} {{vm.price|intcomma}} CHF @@ -130,64 +141,38 @@


-
- +
-
+

-
-

Pre VAT

+
+

{% trans "Pre VAT" %}

-
-

VAT for {{vm.vat_country}} ({{vm.vat_percent}}%)

+
+

{% trans "VAT for" %} {{vm.vat_country}} ({{vm.vat_percent}}%)

-
+

Price

-
+

{{vm.price|intcomma}} CHF

-
+

{{vm.price_with_vat|intcomma}} CHF

{% if vm.discount.amount > 0 %}
-
+

{{vm.discount.name}}

-
+

-{{vm.discount.amount|intcomma}} CHF

-
+

-{{vm.discount.amount_with_vat|intcomma}} CHF

@@ -196,15 +181,15 @@

-
+
-
+

Total

-
+

{{vm.price_after_discount|intcomma}} CHF

-
+

{{vm.price_after_discount_with_vat|intcomma}} CHF

@@ -212,11 +197,9 @@

-
-

+

{% trans "Your Price in Total" %} {{vm.total_price|floatformat:2|intcomma}} CHF -

{% endif %} From 3141dc279326b38cd0025f080b95ec558ea3ebd8 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 30 Jan 2020 11:52:29 +0530 Subject: [PATCH 537/795] Round price_with_vat --- datacenterlight/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 6dec71d2..2f411194 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -681,7 +681,7 @@ class OrderConfirmationView(DetailView, FormView): 2) vm_specs["vat_country"] = user_vat_country vm_specs["discount"] = discount - vm_specs["price_with_vat"] = price + vm_specs["vat"] + vm_specs["price_with_vat"] = round(price + vm_specs["vat"], 2) vm_specs["price_after_discount"] = round(price - discount['amount'], 2) vm_specs["price_after_discount_with_vat"] = round((price - discount['amount_with_vat']) + vm_specs["vat"], 2) request.session['specs'] = vm_specs From 7a9b315e2ed0de42eac0f4dc1199e3ace02abe92 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 30 Jan 2020 12:33:47 +0530 Subject: [PATCH 538/795] Add media query for device width less than 368px --- .../templates/datacenterlight/order_detail.html | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 3236ffe0..525a8a95 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -113,6 +113,15 @@ } } + @media screen and (max-width:367px){ + .cmf-ord-heading { + font-size: 13px; + } + .order-detail-container .order-details { + font-size: 12px; + } + } +
From f4393426d376e73ab13bb3b0cb8602cf5ef731a8 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 1 Feb 2020 08:53:24 +0530 Subject: [PATCH 539/795] Fix proper VAT values --- datacenterlight/views.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 2f411194..8da3e5c5 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -680,10 +680,11 @@ class OrderConfirmationView(DetailView, FormView): vm_specs["total_price"] = round(price + vm_specs["vat"] - discount['amount_with_vat'], 2) vm_specs["vat_country"] = user_vat_country - vm_specs["discount"] = discount - vm_specs["price_with_vat"] = round(price + vm_specs["vat"], 2) + vm_specs["price_with_vat"] = round(price * (1 + vm_specs["vat_percent"]), 2) vm_specs["price_after_discount"] = round(price - discount['amount'], 2) - vm_specs["price_after_discount_with_vat"] = round((price - discount['amount_with_vat']) + vm_specs["vat"], 2) + vm_specs["price_after_discount_with_vat"] = round((price - discount['amount']) * (1 + vm_specs["vat_percent"]), 2) + discount["amount_with_vat"] = vm_specs["price_with_vat"] - vm_specs["price_after_discount_with_vat"] + vm_specs["discount"] = discount request.session['specs'] = vm_specs context.update({ From b1acd3f25b716851ea4f0fd01a60af70f5756c9a Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 1 Feb 2020 09:02:17 +0530 Subject: [PATCH 540/795] Convert VAT percent to CHF before calculations --- datacenterlight/views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 8da3e5c5..943c3565 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -680,10 +680,10 @@ class OrderConfirmationView(DetailView, FormView): vm_specs["total_price"] = round(price + vm_specs["vat"] - discount['amount_with_vat'], 2) vm_specs["vat_country"] = user_vat_country - vm_specs["price_with_vat"] = round(price * (1 + vm_specs["vat_percent"]), 2) + vm_specs["price_with_vat"] = round(price * (1 + vm_specs["vat_percent"] * 0.01), 2) vm_specs["price_after_discount"] = round(price - discount['amount'], 2) - vm_specs["price_after_discount_with_vat"] = round((price - discount['amount']) * (1 + vm_specs["vat_percent"]), 2) - discount["amount_with_vat"] = vm_specs["price_with_vat"] - vm_specs["price_after_discount_with_vat"] + vm_specs["price_after_discount_with_vat"] = round((price - discount['amount']) * (1 + vm_specs["vat_percent"] * 0.01), 2) + discount["amount_with_vat"] = round(vm_specs["price_with_vat"] - vm_specs["price_after_discount_with_vat"], 2) vm_specs["discount"] = discount request.session['specs'] = vm_specs From 838163bd5953cdfde64f89bedd42db22bc5f9c56 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 1 Feb 2020 09:15:12 +0530 Subject: [PATCH 541/795] Make total price consistent --- datacenterlight/views.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 943c3565..37635904 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -677,13 +677,12 @@ class OrderConfirmationView(DetailView, FormView): vm_specs["vat"] = vat vm_specs["vat_percent"] = vat_percent vm_specs["vat_validation_status"] = request.session["vat_validation_status"] if "vat_validation_status" in request.session else "" - vm_specs["total_price"] = round(price + vm_specs["vat"] - discount['amount_with_vat'], - 2) vm_specs["vat_country"] = user_vat_country vm_specs["price_with_vat"] = round(price * (1 + vm_specs["vat_percent"] * 0.01), 2) vm_specs["price_after_discount"] = round(price - discount['amount'], 2) vm_specs["price_after_discount_with_vat"] = round((price - discount['amount']) * (1 + vm_specs["vat_percent"] * 0.01), 2) discount["amount_with_vat"] = round(vm_specs["price_with_vat"] - vm_specs["price_after_discount_with_vat"], 2) + vm_specs["total_price"] = round(vm_specs["price_after_discount_with_vat"]) vm_specs["discount"] = discount request.session['specs'] = vm_specs From e6f00abd71c59dc788c88adb5e17d181dd3a443c Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 1 Feb 2020 09:17:24 +0530 Subject: [PATCH 542/795] Do not round --- datacenterlight/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 37635904..4307fcc3 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -682,7 +682,7 @@ class OrderConfirmationView(DetailView, FormView): vm_specs["price_after_discount"] = round(price - discount['amount'], 2) vm_specs["price_after_discount_with_vat"] = round((price - discount['amount']) * (1 + vm_specs["vat_percent"] * 0.01), 2) discount["amount_with_vat"] = round(vm_specs["price_with_vat"] - vm_specs["price_after_discount_with_vat"], 2) - vm_specs["total_price"] = round(vm_specs["price_after_discount_with_vat"]) + vm_specs["total_price"] = vm_specs["price_after_discount_with_vat"] vm_specs["discount"] = discount request.session['specs'] = vm_specs From 918d2b17e1d476f2311fc9d6f6af5960c2272f57 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 1 Feb 2020 12:13:56 +0530 Subject: [PATCH 543/795] Change invoice url --- .../hosting/virtual_machine_detail.html | 2 +- hosting/views.py | 22 +++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html index 5873a2aa..36c04fed 100644 --- a/hosting/templates/hosting/virtual_machine_detail.html +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -46,7 +46,7 @@
{% trans "Current Pricing" %}
{{order.price|floatformat:2|intcomma}} CHF/{% if order.generic_product %}{% trans order.generic_product.product_subscription_interval %}{% else %}{% trans "Month" %}{% endif %}
- {% trans "See Invoice" %} + {% trans "See Invoice" %}
diff --git a/hosting/views.py b/hosting/views.py index 672868d8..b7bf07c8 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1687,13 +1687,27 @@ class VirtualMachineView(LoginRequiredMixin, View): context = None try: serializer = VirtualMachineSerializer(vm) + hosting_order = HostingOrder.objects.get( + vm_id=serializer.data['vm_id'] + ) + inv_url = None + if hosting_order.subscription_id: + stripe_obj = stripe.Invoice.list( + hosting_order.subscription_id + ) + inv_url = stripe_obj[0].data.hosted_invoice_url + elif hosting_order.stripe_charge_id: + stripe_obj = stripe.Charge.retrieve( + hosting_order.stripe_charge_id + ) + inv_url = stripe_obj.receipt_url context = { 'virtual_machine': serializer.data, - 'order': HostingOrder.objects.get( - vm_id=serializer.data['vm_id'] - ), - 'keys': UserHostingKey.objects.filter(user=request.user) + 'order': hosting_order, + 'keys': UserHostingKey.objects.filter(user=request.user), + 'inv_url': inv_url } + except Exception as ex: logger.debug("Exception generated {}".format(str(ex))) messages.error(self.request, From d8482c52f9d7d487ad1dcab7fa462686020db482 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 1 Feb 2020 12:20:13 +0530 Subject: [PATCH 544/795] Get invoice correctly --- hosting/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index b7bf07c8..1f30ba11 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1693,7 +1693,7 @@ class VirtualMachineView(LoginRequiredMixin, View): inv_url = None if hosting_order.subscription_id: stripe_obj = stripe.Invoice.list( - hosting_order.subscription_id + subscription=hosting_order.subscription_id ) inv_url = stripe_obj[0].data.hosted_invoice_url elif hosting_order.stripe_charge_id: From 9d21181073748d60b1f85759ce44e03a07324215 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 1 Feb 2020 12:23:47 +0530 Subject: [PATCH 545/795] Get stripe invoice obj correctly --- hosting/views.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 1f30ba11..09e1131a 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1693,9 +1693,10 @@ class VirtualMachineView(LoginRequiredMixin, View): inv_url = None if hosting_order.subscription_id: stripe_obj = stripe.Invoice.list( - subscription=hosting_order.subscription_id + subscription=hosting_order.subscription_id, + count=0 ) - inv_url = stripe_obj[0].data.hosted_invoice_url + inv_url = stripe_obj.data[0].hosted_invoice_url elif hosting_order.stripe_charge_id: stripe_obj = stripe.Charge.retrieve( hosting_order.stripe_charge_id From b645f9894bbc0b67ac800353ed5f266fd438412a Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 1 Feb 2020 12:25:51 +0530 Subject: [PATCH 546/795] Open invoice in a new window --- hosting/templates/hosting/virtual_machine_detail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html index 36c04fed..24b2c6ad 100644 --- a/hosting/templates/hosting/virtual_machine_detail.html +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -46,7 +46,7 @@
{% trans "Current Pricing" %}
{{order.price|floatformat:2|intcomma}} CHF/{% if order.generic_product %}{% trans order.generic_product.product_subscription_interval %}{% else %}{% trans "Month" %}{% endif %}
- {% trans "See Invoice" %} + {% trans "See Invoice" %}
From 23b25002aeb713604c8b47e9814e5a8282bdc8d9 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 1 Feb 2020 13:00:04 +0530 Subject: [PATCH 547/795] Take users to invoice url instead of orders Orders need a VAT alignment fix --- datacenterlight/tasks.py | 3 +- .../datacenterlight/order_detail.html | 36 +++++++++---------- datacenterlight/views.py | 2 +- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 8b4626e8..55be8099 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -173,8 +173,7 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): context = { 'base_url': "{0}://{1}".format(user.get('request_scheme'), user.get('request_host')), - 'order_url': reverse('hosting:orders', - kwargs={'pk': order_id}), + 'order_url': reverse('hosting:invoices'), 'page_header': _( 'Your New VM %(vm_name)s at Data Center Light') % { 'vm_name': vm.get('name')}, diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 525a8a95..6f9d43f2 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -103,26 +103,26 @@ {% trans "Product" %}:  {{ request.session.template.name }}

- +

diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 4307fcc3..385cf808 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -1154,7 +1154,7 @@ class OrderConfirmationView(DetailView, FormView): response = { 'status': True, 'redirect': ( - reverse('hosting:orders') + reverse('hosting:invoices') if request.user.is_authenticated() else reverse('datacenterlight:index') ), From 2058c660c0ea364bfb329255bc2ac5601b81204a Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 1 Feb 2020 13:15:03 +0530 Subject: [PATCH 548/795] Retrieve only one invoice --- hosting/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index 09e1131a..729d115b 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1694,7 +1694,7 @@ class VirtualMachineView(LoginRequiredMixin, View): if hosting_order.subscription_id: stripe_obj = stripe.Invoice.list( subscription=hosting_order.subscription_id, - count=0 + count=1 ) inv_url = stripe_obj.data[0].hosted_invoice_url elif hosting_order.stripe_charge_id: From c43afb7c590dccf0ce9e7b8b283d418f3869440b Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 1 Feb 2020 13:47:29 +0530 Subject: [PATCH 549/795] Use round_up method --- datacenterlight/views.py | 10 +++++----- utils/hosting_utils.py | 6 ++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 385cf808..d6bfd27d 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -29,7 +29,7 @@ from utils.forms import ( ) from utils.hosting_utils import ( get_vm_price_with_vat, get_all_public_keys, get_vat_rate_for_country, - get_vm_price_for_given_vat + get_vm_price_for_given_vat, round_up ) from utils.stripe_utils import StripeUtils from utils.tasks import send_plain_email_task @@ -678,10 +678,10 @@ class OrderConfirmationView(DetailView, FormView): vm_specs["vat_percent"] = vat_percent vm_specs["vat_validation_status"] = request.session["vat_validation_status"] if "vat_validation_status" in request.session else "" vm_specs["vat_country"] = user_vat_country - vm_specs["price_with_vat"] = round(price * (1 + vm_specs["vat_percent"] * 0.01), 2) - vm_specs["price_after_discount"] = round(price - discount['amount'], 2) - vm_specs["price_after_discount_with_vat"] = round((price - discount['amount']) * (1 + vm_specs["vat_percent"] * 0.01), 2) - discount["amount_with_vat"] = round(vm_specs["price_with_vat"] - vm_specs["price_after_discount_with_vat"], 2) + vm_specs["price_with_vat"] = round_up(price * (1 + vm_specs["vat_percent"] * 0.01), 2) + vm_specs["price_after_discount"] = round_up(price - discount['amount'], 2) + vm_specs["price_after_discount_with_vat"] = round_up((price - discount['amount']) * (1 + vm_specs["vat_percent"] * 0.01), 2) + discount["amount_with_vat"] = round_up(vm_specs["price_with_vat"] - vm_specs["price_after_discount_with_vat"], 2) vm_specs["total_price"] = vm_specs["price_after_discount_with_vat"] vm_specs["discount"] = discount request.session['specs'] = vm_specs diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index 5d898b0a..7bff9a89 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -1,5 +1,6 @@ import decimal import logging +import math import subprocess from oca.pool import WrongIdError @@ -214,6 +215,11 @@ def get_ip_addresses(vm_id): return "--" +def round_up(n, decimals=0): + multiplier = 10 ** decimals + return math.ceil(n * multiplier) / multiplier + + class HostingUtils: @staticmethod def clear_items_from_list(from_list, items_list): From b103cff0a6742bea0a178914985ae7f29882e1db Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 1 Feb 2020 13:54:29 +0530 Subject: [PATCH 550/795] Dont round_up discount (is obtained from subtraction) --- datacenterlight/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index d6bfd27d..185b3e29 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -681,9 +681,10 @@ class OrderConfirmationView(DetailView, FormView): vm_specs["price_with_vat"] = round_up(price * (1 + vm_specs["vat_percent"] * 0.01), 2) vm_specs["price_after_discount"] = round_up(price - discount['amount'], 2) vm_specs["price_after_discount_with_vat"] = round_up((price - discount['amount']) * (1 + vm_specs["vat_percent"] * 0.01), 2) - discount["amount_with_vat"] = round_up(vm_specs["price_with_vat"] - vm_specs["price_after_discount_with_vat"], 2) + discount["amount_with_vat"] = round(vm_specs["price_with_vat"] - vm_specs["price_after_discount_with_vat"], 2) vm_specs["total_price"] = vm_specs["price_after_discount_with_vat"] vm_specs["discount"] = discount + logger.debug(vm_specs) request.session['specs'] = vm_specs context.update({ From 8fe689d9933ae6c24c29df12b9b52b284ada2db0 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 1 Feb 2020 15:23:26 +0530 Subject: [PATCH 551/795] Update some DE translations --- .../locale/de/LC_MESSAGES/django.po | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po index ebb78a1c..5ba12558 100644 --- a/datacenterlight/locale/de/LC_MESSAGES/django.po +++ b/datacenterlight/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-12-09 12:13+0000\n" +"POT-Creation-Date: 2020-02-01 09:42+0000\n" "PO-Revision-Date: 2018-03-30 23:22+0000\n" "Last-Translator: b'Anonymous User '\n" "Language-Team: LANGUAGE \n" @@ -144,7 +144,6 @@ msgid "" "the heart of Switzerland." msgstr "Bei uns findest Du die günstiges VMs aus der Schweiz." - msgid "Try now, order a VM. VM price starts from only 10.5 CHF per month." msgstr "Unser Angebot beginnt bei 10.5 CHF pro Monat. Probier's jetzt aus!" @@ -407,6 +406,17 @@ msgstr "Datum" msgid "Billed to" msgstr "Rechnungsadresse" +msgid "VAT Number" +msgstr "Mehrwertsteuernummer" + +msgid "Your VAT number has been verified" +msgstr "Deine Mehrwertsteuernummer wurde überprüft" + +msgid "" +"Your VAT number is under validation. VAT will be adjusted, once the " +"validation is complete." +msgstr "" + msgid "Payment method" msgstr "Bezahlmethode" @@ -437,8 +447,14 @@ msgstr "Beschreibung" msgid "Recurring" msgstr "Wiederholend" -msgid "Subtotal" -msgstr "Zwischensumme" +msgid "Price Before VAT" +msgstr "" + +msgid "Pre VAT" +msgstr "" + +msgid "Your Price in Total" +msgstr "" #, fuzzy, python-format #| msgid "" @@ -575,6 +591,9 @@ msgstr "Unser Angebot beginnt bei 15 CHF pro Monat. Probier's jetzt aus!" msgid "Actions speak louder than words. Let's do it, try our VM now." msgstr "Taten sagen mehr als Worte – Teste jetzt unsere VM!" +msgid "See Invoice" +msgstr "" + msgid "Invalid number of cores" msgstr "Ungültige Anzahle CPU-Kerne" @@ -665,6 +684,9 @@ msgstr "" "Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du " "auf sie zugreifen kannst." +#~ msgid "Subtotal" +#~ msgstr "Zwischensumme" + #~ msgid "VAT" #~ msgstr "Mehrwertsteuer" @@ -676,9 +698,6 @@ msgstr "" #~ "ausgelöst, nachdem Du die Bestellung auf der nächsten Seite bestätigt " #~ "hast." -#~ msgid "Card Number" -#~ msgstr "Kreditkartennummer" - #~ msgid "" #~ "You are not making any payment yet. After placing your order, you will be " #~ "taken to the Submit Payment Page." From 88a39ef85a6f9250900a28b0f1156a2fe51cc242 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 1 Feb 2020 15:23:42 +0530 Subject: [PATCH 552/795] Update Changelog for 2.10 --- Changelog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changelog b/Changelog index 566f5960..2c2d73d7 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,8 @@ +2.10: 2020-02-01 + * Feature: Introduce new design to show VAT exclusive/VAT inclusive pricing together + * Feature: Separate VAT and discount in Stripe + * Feature: Show Stripe invoices until we have a better way of showing them elegantly + * Bugfix: Fix bug where VAT is set to 0 because user set a valid VAT number before but later chose not to use any VAT number 2.9.5: 2020-01-20 * Feature: Show invoices directly from stripe (MR!727) 2.9.4: 2020-01-10 From 44dee625b4f0f5099a7ffd8d0dc43520ffe4069c Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 1 Feb 2020 20:24:55 +0530 Subject: [PATCH 553/795] Add some DE translations --- datacenterlight/locale/de/LC_MESSAGES/django.po | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po index 5ba12558..2c9d0587 100644 --- a/datacenterlight/locale/de/LC_MESSAGES/django.po +++ b/datacenterlight/locale/de/LC_MESSAGES/django.po @@ -407,10 +407,10 @@ msgid "Billed to" msgstr "Rechnungsadresse" msgid "VAT Number" -msgstr "Mehrwertsteuernummer" +msgstr "MwSt-Nummer" msgid "Your VAT number has been verified" -msgstr "Deine Mehrwertsteuernummer wurde überprüft" +msgstr "Deine MwSt-Nummer wurde überprüft" msgid "" "Your VAT number is under validation. VAT will be adjusted, once the " @@ -451,7 +451,7 @@ msgid "Price Before VAT" msgstr "" msgid "Pre VAT" -msgstr "" +msgstr "Exkl. MwSt." msgid "Your Price in Total" msgstr "" From c765698a0f96618ee283fa309fa34b82f4e63d9e Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 2 Feb 2020 10:16:45 +0530 Subject: [PATCH 554/795] Add two columns for pricing --- .../datacenterlight/order_detail.html | 133 +++++++++++------- 1 file changed, 86 insertions(+), 47 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 6f9d43f2..80c5f459 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -58,52 +58,7 @@


{% trans "Order summary" %}

- {% if generic_payment_details %} -

- {% trans "Product" %}:  - {{ generic_payment_details.product_name }} -

-
-
- {% if generic_payment_details.vat_rate > 0 %} -

- {% trans "Price" %}: - CHF {{generic_payment_details.amount_before_vat|floatformat:2|intcomma}} -

-

- {% trans "VAT for" %} {{generic_payment_details.vat_country}} ({{generic_payment_details.vat_rate}}%) : - CHF {{generic_payment_details.vat_amount|floatformat:2|intcomma}} -

-

- {% trans "Total Amount" %} : - CHF {{generic_payment_details.amount|floatformat:2|intcomma}} -

- {% else %} -

- {% trans "Amount" %}: - CHF {{generic_payment_details.amount|floatformat:2|intcomma}} -

- {% endif %} - {% if generic_payment_details.description %} -

- {% trans "Description" %}: - {{generic_payment_details.description}} -

- {% endif %} - {% if generic_payment_details.recurring %} -

- {% trans "Recurring" %}: - Yes -

- {% endif %} -
-
- {% else %} -

- {% trans "Product" %}:  - {{ request.session.template.name }} -

- + + {% if generic_payment_details %} +

+ {% trans "Product" %}:  + {{ generic_payment_details.product_name }} +

+ {% if generic_payment_details.description %} +

+ {% trans "Description" %}: + {{generic_payment_details.description}} +

+ {% endif %} + {% if generic_payment_details.recurring %} +

+ {% trans "Recurring" %}: + Yes +

+ {% endif %} +
+
+
+
+
+
+

+ {% trans "Price Before VAT" %} + {{generic_payment_details.amount_before_vat|floatformat:2|intcomma}} CHF +

+
+
+
+
+
+
+
+

+
+
+

{% trans "Pre VAT" %}

+
+
+

{% trans "VAT for" %} {{generic_payment_details.vat_country}} ({{generic_payment_details.vat_percent}}%)

+
+
+
+
+

Price

+
+
+

{{generic_payment_details.amount_before_vat|floatformat:2|intcomma}} CHF

+
+
+

{{generic_payment_details.amount|floatformat:2|intcomma}} CHF

+
+
+
+
+
+
+
+
+
+

Total

+
+
+

{{generic_payment_details.amount_before_vat|floatformat:2|intcomma}} CHF

+
+
+

{{generic_payment_details.amount|floatformat:2|intcomma}} CHF

+
+
+
+
+
+
+
+ {% trans "Your Price in Total" %} + {{generic_payment_details.amount|floatformat:2|intcomma}} CHF +
+
+ {% else %} +

+ {% trans "Product" %}:  + {{ request.session.template.name }} +

From ffde015c3124eb87713ee61ba45644054fd7e14f Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 2 Feb 2020 10:26:13 +0530 Subject: [PATCH 555/795] Fix divs --- .../datacenterlight/order_detail.html | 113 +++++++++--------- 1 file changed, 55 insertions(+), 58 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 80c5f459..3e2ecf04 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -95,68 +95,65 @@ Yes

{% endif %} -
-
-
-
+
+
+
+
+

+ {% trans "Price Before VAT" %} + {{generic_payment_details.amount_before_vat|floatformat:2|intcomma}} CHF +

+
+
+
+
+
+
+
+

+
+
+

{% trans "Pre VAT" %}

+
+
+

{% trans "VAT for" %} {{generic_payment_details.vat_country}} ({{generic_payment_details.vat_rate}}%)

+
-
-

- {% trans "Price Before VAT" %} - {{generic_payment_details.amount_before_vat|floatformat:2|intcomma}} CHF -

+
+
+

Price

+
+
+

{{generic_payment_details.amount_before_vat|floatformat:2|intcomma}} CHF

+
+
+

{{generic_payment_details.amount|floatformat:2|intcomma}} CHF

+
-
-
-
-
-
-
-

-
-
-

{% trans "Pre VAT" %}

-
-
-

{% trans "VAT for" %} {{generic_payment_details.vat_country}} ({{generic_payment_details.vat_percent}}%)

-
-
-
-
-

Price

-
-
-

{{generic_payment_details.amount_before_vat|floatformat:2|intcomma}} CHF

-
-
-

{{generic_payment_details.amount|floatformat:2|intcomma}} CHF

-
-
-
-
-
-
-
-
-
-

Total

-
-
-

{{generic_payment_details.amount_before_vat|floatformat:2|intcomma}} CHF

-
-
-

{{generic_payment_details.amount|floatformat:2|intcomma}} CHF

-
-
-
-
-
-
-
- {% trans "Your Price in Total" %} - {{generic_payment_details.amount|floatformat:2|intcomma}} CHF +
+
+
+
+
+
+
+

Total

+
+
+

{{generic_payment_details.amount_before_vat|floatformat:2|intcomma}} CHF

+
+
+

{{generic_payment_details.amount|floatformat:2|intcomma}} CHF

+
+
+
+
+
+ {% trans "Your Price in Total" %} + {{generic_payment_details.amount|floatformat:2|intcomma}} CHF +
{% else %}

{% trans "Product" %}:  From 9f86f445695a5efc86048a41f37ccb5c60001b0a Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 2 Feb 2020 10:29:33 +0530 Subject: [PATCH 556/795] Add missing divs --- datacenterlight/templates/datacenterlight/order_detail.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 3e2ecf04..078f5efd 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -79,6 +79,8 @@ {% if generic_payment_details %} +

+

{% trans "Product" %}:  {{ generic_payment_details.product_name }} @@ -95,6 +97,7 @@ Yes

{% endif %} +

@@ -154,6 +157,7 @@ {% trans "Your Price in Total" %} {{generic_payment_details.amount|floatformat:2|intcomma}} CHF
+
{% else %}

{% trans "Product" %}:  From fc8c4579fb42197e29da7377b76f4a0b421f38c2 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 2 Feb 2020 10:41:51 +0530 Subject: [PATCH 557/795] Show prices to two decimals --- .../templates/datacenterlight/order_detail.html | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 078f5efd..b8cd7a4b 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -184,7 +184,7 @@

{% trans "Price Before VAT" %} - {{vm.price|intcomma}} CHF + {{vm.price|floatformat:2|intcomma}} CHF

@@ -207,10 +207,10 @@

Price

-

{{vm.price|intcomma}} CHF

+

{{vm.price|floatformat:2|intcomma}} CHF

-

{{vm.price_with_vat|intcomma}} CHF

+

{{vm.price_with_vat|floatformat:2|intcomma}} CHF

{% if vm.discount.amount > 0 %} @@ -219,10 +219,10 @@

{{vm.discount.name}}

-

-{{vm.discount.amount|intcomma}} CHF

+

-{{vm.discount.amount|floatformat:2|intcomma}} CHF

-

-{{vm.discount.amount_with_vat|intcomma}} CHF

+

-{{vm.discount.amount_with_vat|floatformat:2|intcomma}} CHF

{% endif %} @@ -236,10 +236,10 @@

Total

-

{{vm.price_after_discount|intcomma}} CHF

+

{{vm.price_after_discount|floatformat:2|intcomma}} CHF

-

{{vm.price_after_discount_with_vat|intcomma}} CHF

+

{{vm.price_after_discount_with_vat|floatformat:2|intcomma}} CHF

From c0683d9f538769791e3b2fd6623b9ca24a706a61 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 2 Feb 2020 10:57:14 +0530 Subject: [PATCH 558/795] Show prices to two decimal places --- datacenterlight/templatetags/custom_tags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/templatetags/custom_tags.py b/datacenterlight/templatetags/custom_tags.py index 4cc50caa..0574d29f 100644 --- a/datacenterlight/templatetags/custom_tags.py +++ b/datacenterlight/templatetags/custom_tags.py @@ -106,7 +106,7 @@ def get_line_item_from_stripe_invoice(invoice): period = mark_safe("%s — %s" % ( datetime.datetime.fromtimestamp(start_date).strftime('%Y-%m-%d'), datetime.datetime.fromtimestamp(end_date).strftime('%Y-%m-%d'))), - total=invoice.total/100, + total='%.2f' % (invoice.total/100), stripe_invoice_url=invoice.hosted_invoice_url, see_invoice_text=_("See Invoice") )) From e094930d6eec56e523e182e299746601d2e0f046 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 2 Feb 2020 12:39:19 +0530 Subject: [PATCH 559/795] Show product name in invoices list if product is not a VM --- datacenterlight/templatetags/custom_tags.py | 29 +++++++++++++++++++-- hosting/templates/hosting/invoices.html | 2 +- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/datacenterlight/templatetags/custom_tags.py b/datacenterlight/templatetags/custom_tags.py index 0574d29f..cb0fb6fa 100644 --- a/datacenterlight/templatetags/custom_tags.py +++ b/datacenterlight/templatetags/custom_tags.py @@ -1,12 +1,16 @@ import datetime +import logging from django import template from django.core.urlresolvers import resolve, reverse from django.utils.safestring import mark_safe from django.utils.translation import activate, get_language, ugettext_lazy as _ +from hosting.models import GenericProduct from utils.hosting_utils import get_ip_addresses +logger = logging.getLogger(__name__) + register = template.Library() @@ -72,8 +76,10 @@ def get_line_item_from_stripe_invoice(invoice): end_date = 0 is_first = True vm_id = -1 + plan_name = "" for line_data in invoice["lines"]["data"]: if is_first: + plan_name = line_data.plan.name start_date = line_data.period.start end_date = line_data.period.end is_first = False @@ -102,8 +108,9 @@ def get_line_item_from_stripe_invoice(invoice): """.format( vm_id=vm_id if vm_id > 0 else "", - ip_addresses=mark_safe(get_ip_addresses(vm_id)) if vm_id > 0 else "", - period = mark_safe("%s — %s" % ( + ip_addresses=mark_safe(get_ip_addresses(vm_id)) if vm_id > 0 else + mark_safe(plan_name), + period=mark_safe("%s — %s" % ( datetime.datetime.fromtimestamp(start_date).strftime('%Y-%m-%d'), datetime.datetime.fromtimestamp(end_date).strftime('%Y-%m-%d'))), total='%.2f' % (invoice.total/100), @@ -112,3 +119,21 @@ def get_line_item_from_stripe_invoice(invoice): )) else: return "" + + +def get_product_name(plan_name): + product_name = "" + if plan_name and plan_name.startswith("generic-"): + product_id = plan_name[plan_name.index("-") + 1:plan_name.rindex("-")] + try: + product = GenericProduct.objects.get(id=product_id) + product_name = product.product_name + except GenericProduct.DoesNotExist as dne: + logger.error("Generic product id=%s does not exist" % product_id) + product_name = "Unknown" + except GenericProduct.MultipleObjectsReturned as mor: + logger.error("Multiple products with id=%s exist" % product_id) + product_name = "Unknown" + else: + logger.debug("Product name for plan %s does not exist" % plan_name) + return product_name diff --git a/hosting/templates/hosting/invoices.html b/hosting/templates/hosting/invoices.html index f48802d1..1a97fd1f 100644 --- a/hosting/templates/hosting/invoices.html +++ b/hosting/templates/hosting/invoices.html @@ -19,7 +19,7 @@ {% trans "VM ID" %} - {% trans "IP Address" %} + {% trans "IP Address" %}/{% trans "Product" %} {% trans "Period" %} {% trans "Amount" %} From 42a4a77c0278cf2c4336a605f0d9b1715d9ba304 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 2 Feb 2020 12:42:19 +0530 Subject: [PATCH 560/795] Show product name --- datacenterlight/templatetags/custom_tags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/templatetags/custom_tags.py b/datacenterlight/templatetags/custom_tags.py index cb0fb6fa..a2d9bec6 100644 --- a/datacenterlight/templatetags/custom_tags.py +++ b/datacenterlight/templatetags/custom_tags.py @@ -109,7 +109,7 @@ def get_line_item_from_stripe_invoice(invoice): """.format( vm_id=vm_id if vm_id > 0 else "", ip_addresses=mark_safe(get_ip_addresses(vm_id)) if vm_id > 0 else - mark_safe(plan_name), + mark_safe(get_product_name(plan_name)), period=mark_safe("%s — %s" % ( datetime.datetime.fromtimestamp(start_date).strftime('%Y-%m-%d'), datetime.datetime.fromtimestamp(end_date).strftime('%Y-%m-%d'))), From 70a3620598cf06f904c01276470096670b1c069f Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 2 Feb 2020 13:09:27 +0530 Subject: [PATCH 561/795] Fix getting product from plan_name --- datacenterlight/templatetags/custom_tags.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/datacenterlight/templatetags/custom_tags.py b/datacenterlight/templatetags/custom_tags.py index a2d9bec6..d86ba27d 100644 --- a/datacenterlight/templatetags/custom_tags.py +++ b/datacenterlight/templatetags/custom_tags.py @@ -124,13 +124,15 @@ def get_line_item_from_stripe_invoice(invoice): def get_product_name(plan_name): product_name = "" if plan_name and plan_name.startswith("generic-"): - product_id = plan_name[plan_name.index("-") + 1:plan_name.rindex("-")] + first_index_hyphen = plan_name.index("-") + 1 + product_id = plan_name[first_index_hyphen: + (plan_name[first_index_hyphen:].index("-")) + first_index_hyphen] try: product = GenericProduct.objects.get(id=product_id) product_name = product.product_name except GenericProduct.DoesNotExist as dne: logger.error("Generic product id=%s does not exist" % product_id) - product_name = "Unknown" + product_name = plan_name except GenericProduct.MultipleObjectsReturned as mor: logger.error("Multiple products with id=%s exist" % product_id) product_name = "Unknown" From a5d393ad20042770853dcb4c4765dac876b60a4b Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 2 Feb 2020 13:21:01 +0530 Subject: [PATCH 562/795] Right align prices --- datacenterlight/templatetags/custom_tags.py | 2 +- hosting/static/hosting/css/orders.css | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/datacenterlight/templatetags/custom_tags.py b/datacenterlight/templatetags/custom_tags.py index d86ba27d..0cb18e5b 100644 --- a/datacenterlight/templatetags/custom_tags.py +++ b/datacenterlight/templatetags/custom_tags.py @@ -102,7 +102,7 @@ def get_line_item_from_stripe_invoice(invoice): {vm_id} {ip_addresses} {period} - {total} + {total} {see_invoice_text} diff --git a/hosting/static/hosting/css/orders.css b/hosting/static/hosting/css/orders.css index 6819a94b..2deab999 100644 --- a/hosting/static/hosting/css/orders.css +++ b/hosting/static/hosting/css/orders.css @@ -2,4 +2,10 @@ .orders-container .table > tbody > tr > td { vertical-align: middle; +} + +@media screen and (min-width:767px){ + .dcl-text-right { + padding-right: 20px; + } } \ No newline at end of file From f0f8af23674f1be9b0bd353b507a7e33df249a78 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 2 Feb 2020 21:39:51 +0530 Subject: [PATCH 563/795] Change col-sm-8 to col-sm-9 to spread content further on desktop view --- .../datacenterlight/order_detail.html | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index b8cd7a4b..2897e3bf 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -80,7 +80,7 @@ {% if generic_payment_details %}
-
+

{% trans "Product" %}:  {{ generic_payment_details.product_name }} @@ -101,7 +101,7 @@


-
+

{% trans "Price Before VAT" %} {{generic_payment_details.amount_before_vat|floatformat:2|intcomma}} CHF @@ -110,7 +110,7 @@


-
+

@@ -137,7 +137,7 @@

-
+

Total

@@ -153,7 +153,7 @@

-
+
{% trans "Your Price in Total" %} {{generic_payment_details.amount|floatformat:2|intcomma}} CHF
@@ -164,7 +164,7 @@ {{ request.session.template.name }}

-
+

{% trans "Cores" %}: {{vm.cpu|floatformat}} @@ -181,7 +181,7 @@


-
+

{% trans "Price Before VAT" %} {{vm.price|floatformat:2|intcomma}} CHF @@ -190,7 +190,7 @@


-
+

@@ -230,7 +230,7 @@

-
+

Total

@@ -246,7 +246,7 @@

-
+
{% trans "Your Price in Total" %} {{vm.total_price|floatformat:2|intcomma}} CHF
From 1630dc195b96f839d249f2b2d64a97786a20e7aa Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 2 Feb 2020 21:52:48 +0530 Subject: [PATCH 564/795] Reduce header font size --- datacenterlight/templates/datacenterlight/order_detail.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 2897e3bf..5060f1cc 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -61,7 +61,7 @@