From 484bd53bd26c87a1733216b7f875d45f34ba2b84 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 12 Jan 2019 11:02:33 +0100 Subject: [PATCH 1/5] Fix the way we calculate VAT and final VM price 1. Get the VM's pricing from its config and VMPricing 2. Compute and apply discount if any 3. Apply VAT on the discounted amount from 2 --- .../datacenterlight/order_detail.html | 24 +++++++------ datacenterlight/views.py | 8 ++--- hosting/templates/hosting/order_detail.html | 24 +++++++------ hosting/views.py | 30 ++++------------ utils/hosting_utils.py | 35 ++++++++++++------- 5 files changed, 60 insertions(+), 61 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 31933e12..9fd6c9d5 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -98,22 +98,26 @@ {% 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 %} +

+ {% trans "Subtotal" %} + {{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

+

+ {% trans "Subtotal after discount" %} + {{vm.total_after_discount|floatformat:2|intcomma}} CHF +

+ {% endif %} + {% if vm.vat > 0 %} +

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

{% endif %}
diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 5dc3a3d3..d6441205 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -183,7 +183,7 @@ class IndexView(CreateView): ) return HttpResponseRedirect(referer_url + "#order_form") - price, vat, vat_percent, discount = get_vm_price_with_vat( + vm_price_dict = get_vm_price_with_vat( cpu=cores, memory=memory, ssd_size=storage, @@ -193,13 +193,9 @@ class IndexView(CreateView): 'cpu': cores, 'memory': memory, 'disk_size': storage, - 'price': price, - 'vat': vat, - 'vat_percent': vat_percent, - 'discount': discount, - 'total_price': round(price + vat - discount['amount'], 2), 'pricing_name': vm_pricing_name } + specs.update(vm_price_dict) request.session['specs'] = specs request.session['template'] = template_data return HttpResponseRedirect(reverse('datacenterlight:payment')) diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 4a62e9fa..5ba8bc3b 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -135,22 +135,26 @@ {% 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 %} +

+ {% trans "Subtotal" %} + {{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

+

+ {% trans "Subtotal after discount" %} + {{vm.total_after_discount|floatformat:2|intcomma}} CHF +

+ {% endif %} + {% if vm.vat > 0 %} +

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

{% endif %}
diff --git a/hosting/views.py b/hosting/views.py index 32de4e54..eb6ec1a4 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -878,18 +878,14 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): 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( + vm_price_dict = 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'] + context['vm'] = vm_price_dict context['subscription_end_date'] = vm_detail.end_date() except VMDetail.DoesNotExist: try: @@ -898,20 +894,14 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): ) vm = manager.get_vm(obj.vm_id) context['vm'] = VirtualMachineSerializer(vm).data - price, vat, vat_percent, discount = get_vm_price_with_vat( + vm_price_dict = 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'] - ) + context['vm'] = vm_price_dict except WrongIdError: messages.error( self.request, @@ -1290,25 +1280,19 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): extra_tags='storage') return redirect(CreateVirtualMachinesView.as_view()) - price, vat, vat_percent, discount = get_vm_price_with_vat( + vm_price_dict = get_vm_price_with_vat( cpu=cores, memory=memory, ssd_size=storage, pricing_name=vm_pricing_name ) - specs = { 'cpu': cores, 'memory': memory, 'disk_size': storage, - 'discount': discount, - 'price': price, - 'vat': vat, - 'vat_percent': vat_percent, - 'total_price': round(price + vat - discount['amount'], 2), - 'pricing_name': vm_pricing_name + 'pricing_name': vm_pricing_name, } - + specs.update(vm_price_dict) request.session['specs'] = specs request.session['template'] = template_data return redirect(reverse('hosting:payment')) diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index ec97a320..b358ebdb 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -95,8 +95,14 @@ def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0, :param ssd_size: Disk space of the VM (SSD) :param hdd_size: The HDD size :param pricing_name: The pricing name to be used - :return: The a tuple containing the price of the VM, the VAT and the - VAT percentage + :return: A dict containing + 1. the price of the VM, + 2. the applied VAT amount + 3. the VAT percentage + 4. the discount (a dict containing the discount name and + the discount amount), + 5. the price after discount + 6. the final price """ try: pricing = VMPricing.objects.get(name=pricing_name) @@ -109,28 +115,33 @@ def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0, ) return None - price = ( + price = decimal.Decimal( (decimal.Decimal(cpu) * pricing.cores_unit_price) + (decimal.Decimal(memory) * pricing.ram_unit_price) + (decimal.Decimal(ssd_size) * pricing.ssd_unit_price) + (decimal.Decimal(hdd_size) * pricing.hdd_unit_price) ) + discount = { + 'name': pricing.discount_name, + 'amount': round(pricing.discount_amount, 2) + } + price_after_discount = price - discount['amount'] + if pricing.vat_inclusive: vat = decimal.Decimal(0) vat_percent = decimal.Decimal(0) else: - vat = price * pricing.vat_percentage * decimal.Decimal(0.01) + vat = price_after_discount * pricing.vat_percentage * decimal.Decimal(0.01) vat_percent = pricing.vat_percentage - cents = decimal.Decimal('.01') - price = price.quantize(cents, decimal.ROUND_HALF_UP) - vat = vat.quantize(cents, decimal.ROUND_HALF_UP) - discount = { - 'name': pricing.discount_name, - 'amount': round(float(pricing.discount_amount), 2) + return { + 'price': round(price, 2), + 'vat': round(vat, 2), + 'vat_percent': round(vat_percent, 2), + 'discount': discount, + 'price_after_discount': round(price_after_discount, 2), + 'total_price': round(price_after_discount + vat, 2) } - return (round(float(price), 2), round(float(vat), 2), - round(float(vat_percent), 2), discount) def ping_ok(host_ipv6): From 2186ff1250ea443b478d578ebc83e3f833578ec7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 12 Jan 2019 11:40:00 +0100 Subject: [PATCH 2/5] Convert price parameters to float --- utils/hosting_utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index b358ebdb..09b8d3d1 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -123,7 +123,7 @@ def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0, ) discount = { 'name': pricing.discount_name, - 'amount': round(pricing.discount_amount, 2) + 'amount': round(float(pricing.discount_amount), 2) } price_after_discount = price - discount['amount'] @@ -135,12 +135,12 @@ def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0, vat_percent = pricing.vat_percentage return { - 'price': round(price, 2), - 'vat': round(vat, 2), - 'vat_percent': round(vat_percent, 2), + 'price': round(float(price), 2), + 'vat': round(float(vat), 2), + 'vat_percent': round(float(vat_percent), 2), 'discount': discount, - 'price_after_discount': round(price_after_discount, 2), - 'total_price': round(price_after_discount + vat, 2) + 'price_after_discount': round(float(price_after_discount), 2), + 'total_price': round(float(price_after_discount + vat), 2) } From 53d5c66bd141b8ce9dd7383cf29669af9cfa4dac Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 12 Jan 2019 11:44:45 +0100 Subject: [PATCH 3/5] Fix bug Use Decimal discount_amount --- 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 09b8d3d1..0d712d4b 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -125,7 +125,7 @@ def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0, 'name': pricing.discount_name, 'amount': round(float(pricing.discount_amount), 2) } - price_after_discount = price - discount['amount'] + price_after_discount = price - pricing.discount_amount if pricing.vat_inclusive: vat = decimal.Decimal(0) From 8bf89e67116b4c0c8581e1773a8795fd39a357ec Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 12 Jan 2019 11:57:02 +0100 Subject: [PATCH 4/5] Use correct variable total_after_discount -> price_after_discount --- datacenterlight/templates/datacenterlight/order_detail.html | 2 +- hosting/templates/hosting/order_detail.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 9fd6c9d5..645722d6 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -110,7 +110,7 @@

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

{% endif %} {% if vm.vat > 0 %} diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 5ba8bc3b..e91accb1 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -147,7 +147,7 @@

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

{% endif %} {% if vm.vat > 0 %} From e1463127c264d297217a079e94f7649d4e227ab5 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 12 Jan 2019 12:32:31 +0100 Subject: [PATCH 5/5] Fix bug: use dict update instead of replacing key - 'vm' --- hosting/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index eb6ec1a4..615b0774 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -885,7 +885,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): pricing_name=(obj.vm_pricing.name if obj.vm_pricing else 'default') ) - context['vm'] = vm_price_dict + context['vm'].update(vm_price_dict) context['subscription_end_date'] = vm_detail.end_date() except VMDetail.DoesNotExist: try: @@ -901,7 +901,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): pricing_name=(obj.vm_pricing.name if obj.vm_pricing else 'default') ) - context['vm'] = vm_price_dict + context['vm'].update(vm_price_dict) except WrongIdError: messages.error( self.request,