Add the invoice template

This commit is contained in:
amalelshihaby 2021-08-04 12:03:55 +02:00
parent 030a0cd501
commit 5bb0c4cdda
24 changed files with 462 additions and 96 deletions

1
.gitignore vendored
View File

@ -26,3 +26,4 @@ dist/
*.iso
*.sqlite3
.DS_Store
static/CACHE/

View File

@ -0,0 +1,113 @@
body {
font-family: Avenir;
background: white;
font-weight: 500;
line-height: 1.1em;
font-size: 16px;
margin: auto;
}
p {
display: block;
-webkit-margin-before: 14px;
-webkit-margin-after: 14px;
-webkit-margin-start: 0px;
-webkit-margin-end: 0px;
}
.bold {
font-weight: bold;
}
.d1 {
line-height:1.1em;
width: 60%;
float: left;
}
.d2 {
line-height:1.5em;
padding-top: 15px;
width: 40%;
float: left;
}
.d4 {
line-height:1.5em;
width:40%;
float: left;
}
.b1 {
width: 45%;
float: left;
}
.b2 {
width: 55%;
float: left;
text-align: right;
left: 0;
}
.d5 {
width: 100%;
}
.d6 {
width: 68%;
float: left;
font-size: 13px;
}
.d7 {
width: 32%;
float: left;
}
.wf {
width: 100%;
}
hr {
border: 0;
clear:both;
display: inline-block;
width: 100%;
background-color:gray;
height: 1px;
}
.tl {
text-align: left;
margin-left: 5px;
}
.tr {
text-align: right;
margin-right: 5px;
float: right;
}
.tc {
text-align: center;
}
.pc p {
display: block;
-webkit-margin-before: 3px;
-webkit-margin-after: 5px;
-webkit-margin-start: 0px;
-webkit-margin-end: 0px;
}
.th {
border-top: 1px solid gray;
border-bottom: 1px solid gray;
}
.ts {
font-size: 14px;
}
.icon {
width: 16px;
height: 14px;
vertical-align: middle;
margin-right: 2px;
}
.footer {
margin-top: 70px;
font-size: 14px;
}
.footer p {
display: block;
-webkit-margin-before: 5px;
-webkit-margin-after: 5px;
-webkit-margin-start: 0px;
-webkit-margin-end: 0px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -8,7 +8,50 @@ function setBrandIcon(brand) {
brandIconElement.classList.add(pfClass);
}
function fetch_pricing() {
var url = '/pricing/' + $('input[name="pricing_name"]').val() + '/calculate/';
var cores = $('#cores').val();
var memory = $('#memory').val();
var storage = $('#storage').val();
$.ajax({
type: 'GET',
url: url,
data: { cores: cores, memory: memory, storage: storage},
dataType: 'json',
success: function (data) {
if (data && data['total']) {
$('#total').text(data['total']);
}
}
});
};
function incrementValue(e) {
var valueElement = $(e.target).parent().parent().find('input');
var step = $(valueElement).attr('step');
var min = parseInt($(valueElement).attr('min'));
var max = parseInt($(valueElement).attr('max'));
var new_value = 0;
if (e.data.inc == 1) {
new_value = Math.min(parseInt($(valueElement).val()) + parseInt(step) * e.data.inc, max);
} else {
new_value = Math.max(parseInt($(valueElement).val()) + parseInt(step) * e.data.inc, min);
}
$(valueElement).val(new_value);
fetch_pricing();
return false;
};
$(document).ready(function () {
if ($('#pricing_name') != undefined) {
fetch_pricing();
}
$('.fa-plus-circle.right').bind('click', {inc: 1}, incrementValue);
$('.fa-minus-circle.left').bind('click', {inc: -1}, incrementValue);
var hasCreditcard = window.hasCreditcard || false;
if (hasCreditcard && window.stripeKey) {
var stripe = Stripe(window.stripeKey);
@ -154,7 +197,7 @@ $(document).ready(function () {
});
$('#checkout-btn').click(function () {
if($('input[name="payment_card"]:checked').size() == 1) {
if($('input[name="payment_card"]:checked').length == 1) {
var id = $('input[name="payment_card"]:checked').val();
if (id != 'new') {
$('#id_card').val(id);

View File

@ -1,4 +1,4 @@
{% load static i18n %} {% get_current_language as LANGUAGE_CODE %}
{% load static compress i18n %} {% get_current_language as LANGUAGE_CODE %}
<!DOCTYPE html>
<html lang="{{LANGUAGE_CODE}}">
<head>
@ -22,11 +22,13 @@
type="text/css"
/>
<!-- Custom CSS -->
{% compress css %}
<link
href="{% static 'matrixhosting/css/theme.css' %}"
rel="stylesheet"
type="text/css"
/>
{% endcompress %}
{% block css_extra %} {% endblock css_extra %}
<!-- External Fonts -->
@ -55,6 +57,8 @@
<script src="{% static 'matrixhosting/js/bootstrap.bundle.min.js' %}"></script>
<!-- Custom JS -->
{% block js_extra %} {% endblock js_extra %}
<script src="{% static 'matrixhosting/js/theme.js' %}"></script>
{% compress js %}
<script src="{% static 'matrixhosting/js/theme.js' %}"></script>
{% endcompress %}
</body>
</html>

View File

@ -1,49 +1,171 @@
{% load static i18n %}
{% get_current_language as LANGUAGE_CODE %}
<!DOCTYPE html>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Matrix Hosting by ungleich" />
<meta name="author" content="ungleich glarus ag" />
<title>
Hosting Invoice
</title>
<!-- Web Fonts
======================= -->
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Rubik:300,300i,400,400i,500,500i,700,700i,900,900i' type='text/css'>
<!-- Stylesheet
======================= -->
<link href="{% static 'matrixhosting/css/bootstrap.min.css' %}" rel="stylesheet" />
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{%trans "Invoice: " %} {{bill.id}}</title>
<style>
@font-face {
font-family: 'Avenir';
src: url("{{ base_url }}{% static 'matrixhosting/webfonts/Avenir-Book.ttf' %}");
}
</style>
<link href="{{ base_url }}{% static 'matrixhosting/css/invoice.css' %}" rel="stylesheet">
</head>
<body>
<!-- Container -->
<div class="container-fluid invoice-container">
<!-- Header -->
<header>
<div class="row align-items-center">
<div class="col-sm-7 text-center text-sm-start mb-3 mb-sm-0">
<img id="logo" src="{% static 'matrixhosting/images/logo.png' %}" title="matrixhosting" alt="matrixhosting" />
</div>
<div class="col-sm-5 text-center text-sm-end">
<h4 class="text-7 mb-0">Invoice</h4>
</div>
</div>
<hr>
</header>
<!-- Main Content -->
<main>
</main>
<!-- Footer -->
<footer class="text-center mt-4">
</footer>
<div class="invoice-container">
<div class="header" style="line-height:1.1em;">
<div class="d1">
<div class="logo">
<img class="" src="{{ base_url }}{% static 'matrixhosting/images/company-small.jpg' %}" width="50%"/>
</div>
</div>
<div class="d2" name="company_address">
<span class="bold">ungleich glarus ag</span>
<address>
Bahnhofstrasse 1<br>
8783 Linthal<br>
Switzerland
</address>
</div>
<div class="first-page">
<div class="d1" style="margin-top:15px;">
<b>
<span style="font-size: 16px">{{bill.billing_address.full_name}}</span>
</b>
<br/>
<span>{{bill.billing_address.owner.email}}</span>
<address>
{{bill.billing_address.street}}<br>
{{bill.billing_address.city}}<br>
{{bill.billing_address.get_country_display}}
</address>
</div>
</div>
<div class="d4" style="margin-top:15px;">
<div>
<div class="b1">
<span>{%trans "Date of invoice:" %}</span>
</div>
<div class="b2">
<span>{{bill.starting_date|date}}</span>
<br/>
</div>
</div>
<div>
<div class="b1">
<span>{%trans "Invoice Number:" %}</span>
</div>
<div class="b2">
<span>#{{bill.id}}</span>
</div>
</div>
<div>
<div class="b1">
<span>{%trans "Due Date:" %}</span>
</div>
<div class="b2">
<span>{{bill.due_date}}</span>
</div>
</div>
</div>
<div style="clear: both;">
</div>
<div class="d5" style="margin-top:20px; margin-bottom:10px important;">
<br/>
<span style="font-size: 2em !important; font-weight: bolder !important;">{%trans "INVOICE" %}</span>
<span style="font-size: 2em !important; font-weight: bolder !important;padding-left:5px"> {{bill.starting_date|date:"m-Y"}}</span>
</div>
<div style="clear: both;">
<table class="wf" style="margin-top:20px; margin-bottom:10px important;border-top: 1px solid gray;">
<thead >
<tr class="" style="padding-top: 10px; padding-bottom:10px;background-color: #f5f5f5;">
<th class="tl bold" style="padding: 5px 0px 5px 0px !important;">{%trans "Product" %}</th>
<th class="tc bold" style="padding: 5px 0px 5px 0px !important;">{%trans "Quantity" %}</th>
<th class="bold" style="text-align: right; padding: 5px 0px 5px 0px !important;">{%trans "Amount in " %} <span>CHF</span></th>
</tr>
</thead>
{% for record in bill.bill_records.all %}
<tr class="ts" style="line-height:1.8em !important;">
<td class="tl">
<span>{{record.description}}</span>
</td>
<td class="tc">
<span>{{record.quantity}}</span>
</td>
<td style="text-align: right;">
<span>{{record.subtotal}}</span>
</td>
</tr>
{% endfor %}
</table>
<div class="page">
<div class="wf th">
<p class="ts">
<span class="tl">SubTotal</span>
<span class="tr">{{bill.subtotal}}</span>
</span>
</p>
<p class="ts">
<span class="tl">{{vat_rate}}</span>
<span class="tr">{{tax_amount}}
</span>
</p>
</div>
<div class="wf pc" style="background-color: #f5f5f5;padding-top:5px;padding-bottom:5px;">
<p class="bold" style="padding-top:5px;">
<span class="tl" style="font-size: 16px">{%trans "CHF" %}</span>
<span class="tr" style="font-size: 16px">{{bill.sum}}</span>
</p>
</div>
</div>
<div class="footer wf custom_footer">
<br/>
<div class="d6">
<p>
<img class="icon" src="{{ base_url }}{% static 'matrixhosting/images/call.png' %}"/>
<span>+4(144) 534-6622</span>
</p>
<p>
<img class="icon" src="{{ base_url }}{% static 'matrixhosting/images/msg.png' %}"/>
<span>buchhaltung-ag@ungleich.ch</span>
</p>
<p>
<img class="icon" src="{{ base_url }}{% static 'matrixhosting/images/home.png' %}"/>
<span>https://www.ungleich.ch</span>
</p>
<p>
<img class="icon" src="{{ base_url }}{% static 'matrixhosting/images/twitter.png' %}"/>
@ungleich
</p>
</div>
<div class="d7">
<div>
<p>Glarner Kantonalbank</p>
<p>
<span class="bold">IBAN: CH 4300 7730 0055 5931 177</span>
</p>
<p>
<span class="bold">BIC: GLKBCH22</span>
</p>
</div>
<p style="font-size: 13px; white-space: nowrap !important">Mwst-Nummer: CHE-156.970.649 MWST</span></p>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,6 +1,6 @@
{% extends "matrixhosting/base.html" %}
{% load static i18n %}
{% load static compress i18n %}
{% block title %} Request Details {% endblock %}
@ -130,7 +130,7 @@
<p><span>{% trans "VAT for" %} {{pricing.vat_country}} ({{pricing.vat_percent}}%)</span></p>
</div>
<div class="col-md-6 col-sm-6 col-xs-6">
<p><span class="pull-right" > CHF</span></p>
<p><span class="pull-right" > {{pricing.vat_amount}} CHF</span></p>
</div>
</div>
</div>
@ -203,5 +203,7 @@ aria-hidden="true" data-backdrop="static" data-keyboard="false">
<!-- jQuery -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.3/jquery.validate.min.js"></script>
<!-- Custom JS -->
{% compress js %}
<script type="text/javascript" src="{% static 'matrixhosting/js/order.js' %}"></script>
{% endcompress %}
{% endblock js_extra %}

View File

@ -1,6 +1,6 @@
{% extends "matrixhosting/base.html" %}
{% load static i18n %}
{% load static compress i18n %}
{% block title %} Request Details {% endblock %}
@ -196,7 +196,7 @@
{% endif %}
</div>
<div class="col col-lg-6">
<div class="float-right"><span class="text-4">{{request.session.pricing.total|floatformat}}</span><span class="text-2 p-1">CHF</span></div>
<div class="float-right"><span id="total" class="text-4">{{request.session.pricing.total|floatformat}}</span><span class="text-2 p-1">CHF</span></div>
</div>
</div>
<hr class="mt-2 mx-n3">
@ -269,7 +269,9 @@
(function () {
window.stripeKey = "{{stripe_key}}";
window.current_lan = "{{LANGUAGE_CODE}}";
window.hasCreditcard = "{{show_cards}}";
{% if show_cards %}
window.hasCreditcard = true;
{% endif %}
})();
</script>
{%endif%}
@ -278,5 +280,7 @@
<script src="https://js.stripe.com/v3/"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.3/jquery.validate.min.js"></script>
<!-- Custom JS -->
<script type="text/javascript" src="{% static 'matrixhosting/js/payment.js' %}"></script>
{% compress js %}
<script type="text/javascript" src="{% static 'matrixhosting/js/payment.js' %}"></script>
{% endcompress %}
{% endblock js_extra %}

View File

@ -50,7 +50,4 @@
{% endblock %}
{% block js_extra %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.3/jquery.validate.min.js"></script>
<!-- Custom JS -->
<script type="text/javascript" src="{% static 'matrixhosting/js/order.js' %}"></script>
{% endblock js_extra %}

View File

@ -1,7 +1,7 @@
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from wkhtmltopdf.views import PDFTemplateView
from .views import *
app_name = 'matrixhosting'

View File

@ -1,19 +0,0 @@
import os
from io import BytesIO
from django.http import HttpResponse
from django.template.loader import get_template
from django.conf import settings
from xhtml2pdf import pisa
def render_to_pdf(template_src, context_dict={}):
template = get_template(template_src)
html = template.render(context_dict)
result = BytesIO()
# pdf = pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")), result)
links = lambda uri, rel: os.path.join(settings.MEDIA_ROOT, uri.replace(settings.MEDIA_URL, ''))
pdf = pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")),dest=result)
if not pdf.err:
return HttpResponse(result.getvalue(), content_type='application/pdf')
return None

View File

@ -17,6 +17,7 @@ from django.conf import settings
from django.http import (
HttpResponseRedirect, JsonResponse, HttpResponse
)
from wkhtmltopdf.views import PDFTemplateResponse
from rest_framework import viewsets, permissions
from uncloud_pay.models import PricingPlan
@ -27,7 +28,7 @@ from uncloud_pay.selectors import get_billing_address_for_user, has_enough_balan
import uncloud_pay.stripe as uncloud_stripe
from .models import VMInstance
from .serializers import *
from .utils import render_to_pdf
from .utils import *
logger = logging.getLogger(__name__)
@ -195,6 +196,7 @@ class OrderDetailsView(DetailView):
request.session.get('order'))
if order:
bill = Bill.create_next_bill_for_user_address(billing_address)
self.request.session['bill_id'] = bill.id
payment= Payment.withdraw(owner=request.user, amount=total, notes=f"BILL #{bill.id}")
if payment:
#Close the bill as the payment has been added
@ -244,26 +246,40 @@ class OrderSuccessView(DetailView):
'order': self.request.session.get('order'),
'balance': get_balance_for_user(self.request.user)
}
# if ('order' not in request.session):
# return HttpResponseRedirect(reverse('matrix:index'))
if ('order' not in request.session):
return HttpResponseRedirect(reverse('matrix:index'))
return render(request, self.template_name, context)
class InvoiceDownloadView(View):
def get(self, request, *args, **kwargs):
data = {
'today': datetime.date.today(),
'amount': 39.99,
'customer_name': 'Cooper Mann',
'order_id': 1233434,
}
pdf = render_to_pdf('matrixhosting/invoice.html', data)
if pdf:
response = HttpResponse(pdf, content_type='application/pdf')
content = "inline; filename=invoice.pdf"
content = "attachment; filename=invoice.pdf"
response['Content-Disposition'] = content
return response
return HttpResponse("Not found")
template = 'matrixhosting/invoice.html'
filename = 'invoice.pdf'
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
def get_context_data(self, **kwargs):
context = {'base_url': f'{self.request.scheme}://{self.request.get_host()}'}
bill = Bill.objects.get(id=self.request.session.get('bill_id'))
if bill:
context['bill'] = bill
context['vat_rate'] = str(round(bill.vat_rate * 100, 2)) + '%'
context['tax_amount'] = round(bill.vat_rate * bill.subtotal, 2)
return context
def get(self, request):
cmd_options = {
'page_height': 240,
'page_width':175,
'orientation': 'Portrait',
'header_spacing': 65,
'header_line': False
}
return PDFTemplateResponse(request=request,
template=self.template,
filename = self.filename,
cmd_options= cmd_options,
context= self.get_context_data())
class Dashboard(ListView):

View File

@ -7,8 +7,9 @@ fontawesome-free
psycopg2
ldap3
django-allauth
django-compressor
xmltodict
xhtml2pdf
django-wkhtmltopdf
parsedatetime
# Follow are for creating graph models
pyparsing

View File

@ -62,6 +62,8 @@ INSTALLED_APPS = [
'django.contrib.sites',
'django.contrib.staticfiles',
'django_extensions',
'compressor',
'wkhtmltopdf',
'rest_framework',
'django_q',
'notifications',
@ -90,6 +92,7 @@ MIDDLEWARE = [
]
ROOT_URLCONF = 'uncloud.urls'
WKHTMLTOPDF_CMD = "/usr/local/bin/wkhtmltopdf"
TEMPLATES = [
{
@ -188,11 +191,13 @@ USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/
STATIC_URL = '/static/'
STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static") ]
STATIC_ROOT = os.path.join(BASE_DIR, "static")
STATICFILES_FINDERS = [
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
'compressor.finders.CompressorFinder',
]
COMPRESS_ENABLED = True
#VM Deployment TEMPLATE
GITLAB_SERVER = env('GITLAB_SERVER')

View File

@ -0,0 +1,24 @@
# Generated by Django 3.2.4 on 2021-08-03 21:18
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('uncloud_pay', '0024_auto_20210730_1441'),
]
operations = [
migrations.AlterField(
model_name='billrecord',
name='bill',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bill_records', to='uncloud_pay.bill'),
),
migrations.AlterField(
model_name='payment',
name='type',
field=models.CharField(choices=[('withdraw', 'Withdraw Money'), ('deposit', 'Deposit Money')], default='deposit', max_length=256),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 3.2.4 on 2021-08-03 21:40
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('uncloud_pay', '0025_auto_20210803_2118'),
]
operations = [
migrations.RemoveField(
model_name='order',
name='description',
),
]

View File

@ -649,8 +649,6 @@ class Order(models.Model):
customer = models.ForeignKey(StripeCustomer, on_delete=models.CASCADE, null=True)
description = models.TextField()
product = models.ForeignKey(Product, blank=False, null=False, on_delete=models.CASCADE)
config = models.JSONField()
@ -806,6 +804,19 @@ class Order(models.Model):
@property
def is_one_time(self):
return not self.is_recurring
@property
def description(self):
desc = self.product.description + "( "
config = json.loads(self.config)
if config and config["cores"]:
desc = f"{desc} {config['cores']} Cores,"
if config and config["memory"]:
desc = f"{desc} {config['memory']} RAM,"
if config and config["storage"]:
desc = f"{desc} {config['storage']} GB SSD,"
desc += " )"
return desc
def replace_with(self, new_order):
new_order.replaces = self
@ -1011,6 +1022,11 @@ class Bill(models.Model):
bill_records = BillRecord.objects.filter(bill=self)
return sum([ br.sum for br in bill_records ])
@property
def subtotal(self):
bill_records = BillRecord.objects.filter(bill=self)
return sum([ br.subtotal for br in bill_records ])
@property
def vat_rate(self):
return VATRate.get_vat_rate(self.billing_address, when=self.ending_date)
@ -1114,7 +1130,7 @@ class BillRecord(models.Model):
Entry of a bill, dynamically generated from an order.
"""
bill = models.ForeignKey(Bill, on_delete=models.CASCADE)
bill = models.ForeignKey(Bill, on_delete=models.CASCADE, related_name='bill_records')
order = models.ForeignKey(Order, on_delete=models.CASCADE)
creation_date = models.DateTimeField(auto_now_add=True)
@ -1142,12 +1158,30 @@ class BillRecord(models.Model):
else:
return self.order.one_time_price
@property
def description(self):
if self.order:
return self.order.description
return ''
@property
def price(self):
if self.is_recurring_record:
return self.order.recurring_price
else:
return self.order.one_time_price
@property
def subtotal(self):
billing_address_ins = self.order.billing_address
vat_rate = VATRate.get_vat_rate(billing_address_ins)
vat_validation_status = "verified" if billing_address_ins.vat_number_validated_on and billing_address_ins.vat_number_verified else False
config = json.loads(self.order.config)
pricing = uncloud_pay.utils.get_order_total_with_vat(
config["cores"], config["memory"], config["storage"], self.order.pricing_plan.name,
vat_rate=vat_rate * 100, vat_validation_status = vat_validation_status
)
return pricing['subtotal_after_discount']
def __str__(self):
if self.is_recurring_record:

View File

@ -124,10 +124,11 @@ def apply_vat_discount(subtotal, pricing_plan, vat_rate=False, vat_validation_st
'amount_with_vat': round(float(discount_amount_with_vat), 2)
}
subtotal_after_discount = subtotal - discount["amount"]
vat_amount = round(vat_percent * 0.01 * subtotal_after_discount, 2)
price_after_discount_with_vat = round((subtotal - discount['amount']) * (1 + vat_percent * 0.01), 2)
return (subtotal, round(float(subtotal_after_discount), 2), price_after_discount_with_vat,
round(float(vat), 2), vat_percent, discount)
round(float(vat), 2), vat_percent, vat_amount, discount)
def get_order_total_with_vat(cores, memory, storage,
@ -149,13 +150,14 @@ def get_order_total_with_vat(cores, memory, storage,
(decimal.Decimal(memory) * pricing.ram_unit_price) +
(decimal.Decimal(storage) * (pricing.storage_unit_price))
)
subtotal, subtotal_after_discount, price_after_discount_with_vat, vat, vat_percent, discount = \
subtotal, subtotal_after_discount, price_after_discount_with_vat, vat, vat_percent, vat_amount, discount = \
apply_vat_discount(subtotal, pricing, vat_rate, vat_validation_status)
return {
"name": pricing.name,
"subtotal": subtotal,
"discount": discount,
"vat": vat, "vat_percent": vat_percent,
'vat_amount': vat_amount,
"vat_validation_status": vat_validation_status,
"subtotal_after_discount": subtotal_after_discount,
"total": price_after_discount_with_vat