forked from uncloud/uncloud
Task #9611: Add support for writing DNS entries matrix.ungleich.cloud and matrix.0co2.cloud
This commit is contained in:
parent
5bb0c4cdda
commit
7986b825a7
31 changed files with 626 additions and 478 deletions
|
@ -5,6 +5,7 @@ from django.forms import ModelForm
|
|||
from django.utils.translation import get_language, ugettext_lazy as _
|
||||
from django.core.exceptions import ValidationError
|
||||
from .validators import domain_name_validator
|
||||
from .models import VMInstance
|
||||
from uncloud_pay.models import BillingAddress
|
||||
|
||||
|
||||
|
@ -37,7 +38,24 @@ class InitialRequestForm(MainForm):
|
|||
storage = forms.IntegerField(label='Storage', min_value=100, max_value=10000, initial=100)
|
||||
pricing_name = forms.CharField(required=True)
|
||||
|
||||
class RequestHostedVMForm(InitialRequestForm):
|
||||
class RequestDomainsNamesForm(MainForm):
|
||||
homeserver_name = forms.CharField(required=True, widget=forms.TextInput(attrs={'placeholder': 'Homeserver Name *'}))
|
||||
webclient_name = forms.CharField(required=True, widget=forms.TextInput(attrs={'placeholder': 'Webclient Name *'}))
|
||||
is_open_registration = forms.BooleanField(required=False, initial=False)
|
||||
|
||||
def clean_homeserver_name(self):
|
||||
homeserver_name = self.cleaned_data['homeserver_name']
|
||||
if VMInstance.objects.filter(homeserver_domain=f"{homeserver_name}.matrix.ungleich.cloud").exists():
|
||||
raise ValidationError("homeserver name already exists")
|
||||
return homeserver_name
|
||||
|
||||
def clean_webclient_name(self):
|
||||
webclient_name = self.cleaned_data['webclient_name']
|
||||
if VMInstance.objects.filter(webclient_domain=f"{webclient_name}.matrix.0co2.cloud").exists():
|
||||
raise ValidationError("webclient name already exists")
|
||||
return webclient_name
|
||||
|
||||
class RequestDomainsForm(MainForm):
|
||||
matrix_domain = DomainNameField(required=True, widget=forms.TextInput(attrs={'placeholder': 'Matrix Domain *'}))
|
||||
homeserver_domain = DomainNameField(required=True, widget=forms.TextInput(attrs={'placeholder': 'Homeserver Domain *'}))
|
||||
webclient_domain = DomainNameField(required=True, widget=forms.TextInput(attrs={'placeholder': 'Webclient Domain *'}))
|
||||
|
|
22
matrixhosting/migrations/0010_auto_20210806_1511.py
Normal file
22
matrixhosting/migrations/0010_auto_20210806_1511.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Generated by Django 3.2.4 on 2021-08-06 15:11
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('matrixhosting', '0009_vminstance_vm_id'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='vminstance',
|
||||
name='vm_id',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='vminstance',
|
||||
name='vm_name',
|
||||
field=models.CharField(blank=True, max_length=256, null=True),
|
||||
),
|
||||
]
|
18
matrixhosting/migrations/0011_alter_vminstance_vm_name.py
Normal file
18
matrixhosting/migrations/0011_alter_vminstance_vm_name.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.4 on 2021-08-06 15:14
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('matrixhosting', '0010_auto_20210806_1511'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='vminstance',
|
||||
name='vm_name',
|
||||
field=models.CharField(editable=False, max_length=253, unique=True),
|
||||
),
|
||||
]
|
23
matrixhosting/migrations/0012_auto_20210808_1651.py
Normal file
23
matrixhosting/migrations/0012_auto_20210808_1651.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 3.2.4 on 2021-08-08 16:51
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('matrixhosting', '0011_alter_vminstance_vm_name'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='vminstance',
|
||||
name='homeserver_domain',
|
||||
field=models.CharField(blank=True, max_length=253, null=True, unique=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='vminstance',
|
||||
name='webclient_domain',
|
||||
field=models.CharField(blank=True, max_length=253, null=True, unique=True),
|
||||
),
|
||||
]
|
23
matrixhosting/migrations/0013_auto_20210808_1652.py
Normal file
23
matrixhosting/migrations/0013_auto_20210808_1652.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 3.2.4 on 2021-08-08 16:52
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('matrixhosting', '0012_auto_20210808_1651'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='vminstance',
|
||||
name='homeserver_domain',
|
||||
field=models.CharField(blank=True, max_length=253, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vminstance',
|
||||
name='webclient_domain',
|
||||
field=models.CharField(blank=True, max_length=253, unique=True),
|
||||
),
|
||||
]
|
|
@ -1,9 +1,10 @@
|
|||
import logging
|
||||
import uuid
|
||||
import os
|
||||
import json
|
||||
import sys
|
||||
import gitlab
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
import yaml
|
||||
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
|
@ -21,7 +22,11 @@ class VMInstance(models.Model):
|
|||
on_delete=models.CASCADE,
|
||||
editable=True)
|
||||
|
||||
vm_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
|
||||
vm_name = models.CharField(max_length=253, editable=False, unique=True)
|
||||
|
||||
homeserver_domain = models.CharField(max_length=253, unique=True, blank=True)
|
||||
|
||||
webclient_domain = models.CharField(max_length=253, unique=True, blank=True)
|
||||
|
||||
config = models.JSONField(null=False, blank=False)
|
||||
|
||||
|
@ -32,24 +37,18 @@ class VMInstance(models.Model):
|
|||
termination_date = models.DateTimeField(blank=True, null=True)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# Read the deployment yaml file and render the template
|
||||
# Then save it as new yaml file and push it to github repo
|
||||
# Save it as new yaml file and push it to github repo
|
||||
if 'test' in sys.argv:
|
||||
return super().save(*args, **kwargs)
|
||||
template_dir = os.path.join(os.path.dirname(__file__), 'yaml')
|
||||
env = Environment(loader = FileSystemLoader(template_dir),autoescape = True)
|
||||
tmpl = env.get_template('deployment.yaml.tmpl')
|
||||
result = tmpl.render(
|
||||
name=self.vm_id
|
||||
)
|
||||
result = yaml.dump(self.config)
|
||||
gl = gitlab.Gitlab(settings.GITLAB_SERVER, oauth_token=settings.GITLAB_OAUTH_TOKEN)
|
||||
project = gl.projects.get(settings.GITLAB_PROJECT_ID)
|
||||
project.files.create({'file_path': settings.GITLAB_YAML_DIR + f'{self.vm_id}.yaml',
|
||||
project.files.create({'file_path': settings.GITLAB_YAML_DIR + f'matrix-{self.vm_name}.yaml',
|
||||
'branch': 'master',
|
||||
'content': result,
|
||||
'author_email': settings.GITLAB_AUTHOR_EMAIL,
|
||||
'author_name': settings.GITLAB_AUTHOR_NAME,
|
||||
'commit_message': f'Add New Deployment for {self.vm_id}'})
|
||||
'commit_message': f'Add New Deployment for matrix-{self.vm_name}'})
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
|
@ -59,11 +58,11 @@ class VMInstance(models.Model):
|
|||
return super().delete(*args, **kwargs)
|
||||
gl = gitlab.Gitlab(settings.GITLAB_SERVER, oauth_token=settings.GITLAB_OAUTH_TOKEN)
|
||||
project = gl.projects.get(settings.GITLAB_PROJECT_ID)
|
||||
f_path = settings.GITLAB_YAML_DIR + f'{self.vm_id}.yaml'
|
||||
f_path = settings.GITLAB_YAML_DIR + f'matrix-{self.vm_name}.yaml'
|
||||
file = project.files.get(file_path=f_path, ref='master')
|
||||
if file:
|
||||
project.files.delete(file_path=f_path,
|
||||
commit_message=f'Delete {self.vm_id}', branch='master',
|
||||
commit_message=f'Delete matrix-{self.vm_name}', branch='master',
|
||||
author_email=settings.GITLAB_AUTHOR_EMAIL,
|
||||
author_name=settings.GITLAB_AUTHOR_NAME)
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import json
|
||||
from matrixhosting.models import VMInstance
|
||||
from uncloud_pay.models import Order
|
||||
from django.db.models.signals import post_save
|
||||
|
@ -5,6 +6,14 @@ from django.dispatch import receiver
|
|||
|
||||
@receiver(post_save, sender=Order)
|
||||
def create_instance(sender, instance, created, **kwargs):
|
||||
if not created:
|
||||
return
|
||||
machine = VMInstance.objects.filter(order=instance).first()
|
||||
if not machine:
|
||||
VMInstance.objects.create(owner=instance.owner, order=instance, config=instance.config)
|
||||
order_config = json.loads(instance.config)
|
||||
instance_config = {'cpuCores': order_config['cores'], 'ram': order_config['memory'], 'storage': order_config['storage'],
|
||||
'matrixDomain': order_config['matrix_domain'], 'homeserverDomain': order_config['homeserver_domain'],
|
||||
'webClientDomain': order_config['webclient_domain'], 'isOpenRegistration': order_config['is_open_registration']}
|
||||
VMInstance.objects.create(owner=instance.owner, order=instance, vm_name=order_config['homeserver_domain'],
|
||||
homeserver_domain=order_config['homeserver_domain'],webclient_domain=order_config['webclient_domain'],
|
||||
config=instance_config)
|
|
@ -24,6 +24,7 @@ p {
|
|||
.d2 {
|
||||
line-height:1.5em;
|
||||
padding-top: 15px;
|
||||
font-style: normal;
|
||||
width: 40%;
|
||||
float: left;
|
||||
}
|
||||
|
|
|
@ -24,12 +24,7 @@ $( document ).ready(function() {
|
|||
$('#createvm-modal-title').text("Error Occurred");
|
||||
$('#createvm-modal-body').html(data.error.message);
|
||||
} else {
|
||||
// The payment has succeeded
|
||||
// Display a success message
|
||||
window.location.href = '/order/success/';
|
||||
// modal_btn.attr('href', data.redirect).removeClass('sr-only sr-only-focusable');
|
||||
// $('#createvm-modal-title').text("Order Succeeded");
|
||||
// $('#createvm-modal-body').html("Order has been added and the instance will be ready soon");
|
||||
window.location.href = data.redirect;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -20,7 +20,8 @@ function fetch_pricing() {
|
|||
dataType: 'json',
|
||||
success: function (data) {
|
||||
if (data && data['total']) {
|
||||
$('#total').text(data['total']);
|
||||
$('#total').text(data['total'] + " CHF");
|
||||
$('#recurring_price').text(data['recurring_price'] + " CHF");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
6
matrixhosting/static/matrixhosting/js/table.js
Normal file
6
matrixhosting/static/matrixhosting/js/table.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
$(document).ready(function () {
|
||||
$('input[name="filter"]').change(function(e) {
|
||||
$(location).attr('href', $(e.target).data('url'));
|
||||
});
|
||||
|
||||
});
|
|
@ -1,127 +0,0 @@
|
|||
{% extends "matrixhosting/base.html" %} {% load static i18n %}
|
||||
{% block content%}
|
||||
<!-- Page Content -->
|
||||
{% csrf_token %}
|
||||
<div>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Description</th>
|
||||
<th scope="col">Starting At</th>
|
||||
<th scope="col">Config</th>
|
||||
<th scope="col">Pricing Plan</th>
|
||||
<th scope="col">OneTime Price</th>
|
||||
<th scope="col">Recurring Price</th>
|
||||
<th scope="col">Ending At</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for object in object_list %}
|
||||
<tr data-id="{{object.id}}">
|
||||
<th scope="row">{{ object.id }}</th>
|
||||
<td>{{ object.description }}</td>
|
||||
<td>{{ object.starting_date }}</td>
|
||||
<td>{{ object.config }}</td>
|
||||
<td>{{ object.pricing_plan}}</td>
|
||||
<td>{{ object.one_time_price }}</td>
|
||||
<td>{{ object.recurring_price }}</td>
|
||||
<td>{{ object.ending_date }}</td>
|
||||
{% if object.ending_date %}
|
||||
<td></td>
|
||||
{% else %}
|
||||
<td>
|
||||
<button
|
||||
class="btn btn-danger btn-sm cancel-subscription"
|
||||
type="submit"
|
||||
name="action"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="modal fade"
|
||||
tabindex="-1"
|
||||
role="dialog"
|
||||
aria-labelledby="mySmallModalLabel"
|
||||
aria-hidden="true"
|
||||
id="mi-modal"
|
||||
>
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="myModalLabel">Cancel Subscription</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
Are you sure that you want to cancel this subscription?. </p>
|
||||
<p>
|
||||
The instance will be active till the end date of the last bill and will be deleted
|
||||
after that.
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" id="modal-btn-yes">
|
||||
Yes
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" id="modal-btn-no">
|
||||
No
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert" role="alert" id="result"></div>
|
||||
<!-- /.banner -->
|
||||
{% endblock %}
|
||||
|
||||
{% block js_extra %}
|
||||
<script type="text/javascript">
|
||||
var modalConfirm = function (callback) {
|
||||
$(".cancel-subscription").on("click", function (event) {
|
||||
$('.selected').removeClass('selected');
|
||||
$(event.target).parent().parent().addClass('selected');
|
||||
$("#mi-modal").modal("show");
|
||||
});
|
||||
|
||||
$("#modal-btn-yes").on("click", function () {
|
||||
callback(true);
|
||||
});
|
||||
|
||||
$("#modal-btn-no").on("click", function () {
|
||||
callback(false);
|
||||
$("#mi-modal").modal("hide");
|
||||
});
|
||||
};
|
||||
|
||||
modalConfirm(function (confirm) {
|
||||
if (confirm) {
|
||||
var selected_order = $('.selected').data('id');
|
||||
$.ajax({
|
||||
url: '{% url "matrix:dashboard" %}',
|
||||
type: 'POST',
|
||||
data: {'order_id': selected_order, 'csrfmiddlewaretoken': '{{ csrf_token }}',},
|
||||
success: function (data) {
|
||||
$("#mi-modal").modal("hide");
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -17,17 +17,17 @@
|
|||
<!-- Primary Navigation
|
||||
============================== -->
|
||||
{% url 'matrix:index' as index_url %}
|
||||
{% url 'matrix:payments' as payments_url %}
|
||||
{% url 'matrix:billing' as payments_url %}
|
||||
<nav class="primary-menu navbar navbar-expand-lg">
|
||||
<div id="header-nav" class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="{% if request.path == index_url %}active{%endif%}"><a href="{{index_url}}">Home</a></li>
|
||||
<li class="{% if request.path == index_url %}active{%endif%}"><a href="{{index_url}}">{%trans "Home" %}</a></li>
|
||||
{% if not request.user.is_authenticated %}
|
||||
<li><a href="">Pricing</a></li>
|
||||
<li><a href="">Contact Us</a></li>
|
||||
{% else %}
|
||||
<li><a href=>Dashboard</a></li>
|
||||
<li class="{% if request.path == payments_url %}active{%endif%}"><a href="{{payments_url}}">Payments</a></li>
|
||||
<li class="{% if request.path == payments_url %}active{%endif%}"><a href="{{payments_url}}">{%trans "Payments" %}</a></li>
|
||||
<li><a href="">Help</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
@ -65,11 +65,11 @@
|
|||
<ul class="dropdown-menu">
|
||||
<li class="text-center text-3 py-2">Hi, {{request.user.username}}</li>
|
||||
<li class="dropdown-divider mx-n3"></li>
|
||||
<li><a class="dropdown-item" href=""><i class="fas fa-user"></i>My Profile</a></li>
|
||||
<li><a class="dropdown-item" href=""><i class="fas fa-shopping-cart"></i>Orders</a></li>
|
||||
<li><a class="dropdown-item" href=""><i class="fas fa-file-invoice"></i>Bills</a></li>
|
||||
<li><a class="dropdown-item" href=""><i class="fas fa-credit-card"></i>Payment Methods</a></li>
|
||||
<li><a class="dropdown-item" href=""><i class="fas fa-cloud"></i>Instances</a></li>
|
||||
<li><a class="dropdown-item" href=""><i class="fas fa-user"></i>{%trans "My Profile" %}</a></li>
|
||||
<li><a class="dropdown-item" href=""><i class="fas fa-tachometer-alt"></i>{%trans "Dashboard" %}</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'matrix:orders' %}"><i class="fas fa-shopping-cart"></i>{%trans "Orders" %}</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'matrix:billing' %}"><i class="fas fa-file-invoice"></i>{%trans "Billing" %}</a></li>
|
||||
<li><a class="dropdown-item" href=""><i class="fas fa-cloud"></i>{%trans "Instances" %}</a></li>
|
||||
<li class="dropdown-divider mx-n3"></li>
|
||||
<li><a class="dropdown-item" href=""><i class="fas fa-life-ring"></i>Need Help?</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'account_logout' %}"><i class="fas fa-sign-out-alt"></i>Sign Out</a></li>
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
{% load static i18n %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
font-family: Avenir;
|
||||
font-weight: 500;
|
||||
line-height: 1.1em;
|
||||
font-size: 16px;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
.d6 {
|
||||
width: 68%;
|
||||
float: left;
|
||||
font-size: 13px;
|
||||
}
|
||||
.wf {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="footer wf custom_footer">
|
||||
<br/>
|
||||
<div class="d6">
|
||||
<p>
|
||||
<img class="icon" src="{{ base_url }}{% static 'matrixhosting/images/call.png' %}"/>
|
||||
<span>+41 55 505 6266</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>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -256,53 +256,6 @@
|
|||
<a href="https://ungleich.ch/u/projects/open-chat/" class="btn btn-light">A free test ride on ungleich Matrix</a> </div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Testimonial
|
||||
============================================= -->
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2 class="text-9 text-center">What people say about MatrixHosting</h2>
|
||||
<p class="lead text-center mb-4">Quidam lisque persius interesset his et, in quot quidam</p>
|
||||
<div class="row">
|
||||
<div class="col-lg-10 col-xl-8 mx-auto">
|
||||
<div class="owl-carousel owl-theme" data-autoplay="true" data-nav="true" data-loop="true" data-margin="30" data-stagepadding="5" data-items-xs="1" data-items-sm="1" data-items-md="1" data-items-lg="1">
|
||||
<div class="item">
|
||||
<div class="testimonial rounded text-center p-4">
|
||||
<p class="text-4">“Easy to use, reasonably priced simply dummy text of the printing and typesetting industry. Quidam lisque persius interesset his et, in quot quidam possim iriure.”</p>
|
||||
<strong class="d-block font-weight-500">Jay Shah</strong> <span class="text-muted">Founder at Icomatic Pvt Ltd</span> </div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="testimonial rounded text-center p-4">
|
||||
<p class="text-4">“I am happy Working with printing and typesetting industry. Quidam lisque persius interesset his et, in quot quidam persequeris essent possim iriure.”</p>
|
||||
<strong class="d-block font-weight-500">Patrick Cary</strong> <span class="text-muted">Freelancer from USA</span> </div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="testimonial rounded text-center p-4">
|
||||
<p class="text-4">“Quidam lisque persius interesset his et, in quot quidam.”</p>
|
||||
<strong class="d-block font-weight-500">De Mortel</strong> <span class="text-muted">Online Retail</span> </div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="testimonial rounded text-center p-4">
|
||||
<p class="text-4">“I have used them twice now. Good rates, very efficient service and it denies high street banks an undeserved windfall. Excellent.”</p>
|
||||
<strong class="d-block font-weight-500">Chris Tom</strong> <span class="text-muted">User from UK</span> </div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="testimonial rounded text-center p-4">
|
||||
<p class="text-4">“Quidam lisque persius interesset his et, in quot quidam”</p>
|
||||
<strong class="d-block font-weight-500">Mauri Lindberg</strong> <span class="text-muted">Freelancer from Australia</span> </div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="testimonial rounded text-center p-4">
|
||||
<p class="text-4">“Only trying it out since a few days. But up to now excellent. Seems to work flawlessly.”</p>
|
||||
<strong class="d-block font-weight-500">Dennis Jacques</strong> <span class="text-muted">User from USA</span> </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center mt-4"><a href="#" class="btn-link text-4">See more people review<i class="fas fa-chevron-right text-2 ml-2"></i></a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<!-- Testimonial end -->
|
||||
|
||||
<!-- Frequently asked questions
|
||||
============================================= -->
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
</div>
|
||||
<div class="d2" name="company_address">
|
||||
<span class="bold">ungleich glarus ag</span>
|
||||
<address>
|
||||
<address style="font-style: normal;">
|
||||
Bahnhofstrasse 1<br>
|
||||
8783 Linthal<br>
|
||||
Switzerland
|
||||
|
@ -36,14 +36,13 @@
|
|||
</div>
|
||||
<div class="first-page">
|
||||
|
||||
<div class="d1" style="margin-top:15px;">
|
||||
<div class="d1">
|
||||
<b>
|
||||
<span style="font-size: 16px">{{bill.billing_address.full_name}}</span>
|
||||
</b>
|
||||
<br/>
|
||||
|
||||
<span>{{bill.billing_address.owner.email}}</span>
|
||||
<address>
|
||||
<address style="font-style: normal;">
|
||||
{{bill.billing_address.street}}<br>
|
||||
{{bill.billing_address.city}}<br>
|
||||
{{bill.billing_address.get_country_display}}
|
||||
|
@ -51,17 +50,7 @@
|
|||
</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="bold">
|
||||
<div class="b1">
|
||||
<span>{%trans "Invoice Number:" %}</span>
|
||||
</div>
|
||||
|
@ -69,24 +58,27 @@
|
|||
<span>#{{bill.id}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="b1">
|
||||
<span>{%trans "Date of invoice:" %}</span>
|
||||
</div>
|
||||
|
||||
<div class="b2">
|
||||
<span>{{bill.starting_date|date:"Y-m-d"}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="b1">
|
||||
<span>{%trans "Due Date:" %}</span>
|
||||
|
||||
</div>
|
||||
<div class="b2">
|
||||
<span>{{bill.due_date}}</span>
|
||||
<span>{{bill.due_date|date:"Y-m-d"}}</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 >
|
||||
|
@ -118,52 +110,18 @@
|
|||
</span>
|
||||
</p>
|
||||
<p class="ts">
|
||||
<span class="tl">{{vat_rate}}</span>
|
||||
<span class="tl">{{bill.billing_address.get_country_display}} VAT {{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="tl" style="font-size: 16px">{%trans "Total 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>
|
||||
|
||||
|
|
|
@ -8,29 +8,29 @@
|
|||
<div class="container">
|
||||
<!-- Steps Progress bar -->
|
||||
<div class="row mt-3 mb-4">
|
||||
<div class="col-lg-12 mx-auto mb-4">
|
||||
<div class="row widget-steps">
|
||||
<div class="col-4 step complete">
|
||||
<div class="step-name">{%trans "Details" %}</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar"></div>
|
||||
</div>
|
||||
<a href="#" class="step-dot"></a> </div>
|
||||
<div class="col-4 step active">
|
||||
<div class="step-name">{%trans "Confirm" %}</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar"></div>
|
||||
</div>
|
||||
<a href="#" class="step-dot"></a> </div>
|
||||
<div class="col-4 step ">
|
||||
<div class="step-name">{%trans "Success" %}</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar"></div>
|
||||
</div>
|
||||
<a href="#" class="step-dot"></a> </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-9 col-lg-8 col-xl-7 mx-auto">
|
||||
<div class="col-lg-12 mx-auto mb-4">
|
||||
<div class="row widget-steps">
|
||||
<div class="col-4 step complete">
|
||||
<div class="step-name">{%trans "Details" %}</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar"></div>
|
||||
</div>
|
||||
<a href="#" class="step-dot"></a> </div>
|
||||
<div class="col-4 step active">
|
||||
<div class="step-name">{%trans "Confirm" %}</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar"></div>
|
||||
</div>
|
||||
<a href="#" class="step-dot"></a> </div>
|
||||
<div class="col-4 step ">
|
||||
<div class="step-name">{%trans "Success" %}</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar"></div>
|
||||
</div>
|
||||
<a href="#" class="step-dot"></a> </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-9 col-lg-8 col-xl-7 mx-auto">
|
||||
<div id="order-detail{{order.pk}}" class="bg-white shadow-sm rounded p-4 mb-4">
|
||||
{% if messages %}
|
||||
<div class="alert alert-warning">
|
||||
|
@ -148,51 +148,52 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="">
|
||||
</div>
|
||||
<form id="virtual_machine_create_form" action="" method="POST">
|
||||
{% csrf_token %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
By clicking "Place order" you agree to our <a href="">Terms of Service</a> and this plan will charge your account balance with {{pricing.total|floatformat:2}} CHF
|
||||
</div>
|
||||
<div class="col-sm-12 order-confirm-btn text-right">
|
||||
<button class="btn choice-btn btn-primary" id="btn-create-vm" data-toggle="modal" data-target="#createvm-modal">
|
||||
{% trans "Confirm Order" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 col-lg-4 col-xl-5 mx-auto">
|
||||
<div class="row bg-white shadow-sm rounded p-3 p-4 pb-sm-4 mb-2">
|
||||
<hr>
|
||||
<form id="virtual_machine_create_form" action="" method="POST">
|
||||
{% csrf_token %}
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-12">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
{{domains_form.homeserver_name}}
|
||||
<div class="input-group-append"><span class="input-group-text">.matrix.ungleich.cloud</span></div>
|
||||
</div>
|
||||
{{ domains_form.homeserver_name.errors }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
{{domains_form.webclient_name}}
|
||||
<div class="input-group-append"><span class="input-group-text">.matrix.0co2.cloud</span></div>
|
||||
</div>
|
||||
{{ domains_form.webclient_name.errors }}
|
||||
</div>
|
||||
<div class="form-check custom-control custom-checkbox">
|
||||
{{domains_form.is_open_registration}}
|
||||
<label class="custom-control-label" for="{{ domains_form.is_open_registration.id_for_label}}">{% trans "Is Open registration possible?" %}</label>
|
||||
{{ domains_form.is_open_registration.errors }}
|
||||
</div>
|
||||
<hr>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
By clicking "Place order" you agree to our <a href="">Terms of Service</a> and this plan will charge your account balance with {{pricing.total|floatformat:2}} CHF
|
||||
</div>
|
||||
<div class="col-sm-12 order-confirm-btn mt-2 text-right">
|
||||
<button class="btn choice-btn btn-primary" id="btn-create-vm" type="submit">
|
||||
{% trans "Confirm Order" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="createvm-modal" tabindex="-1" role="dialog"
|
||||
aria-hidden="true" data-backdrop="static" data-keyboard="false">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header"><h5 class="createvm-modal-title">{% trans "Order Processing..." %}</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row align-items-center flex-row">
|
||||
<div class="modal-icon col col-sm-12 text-center">
|
||||
<i class="fa fa-cog fa-spin fa-3x fa-fw"></i>
|
||||
<span class="sr-only">{% trans "Processing..." %}</span>
|
||||
</div>
|
||||
<div class="modal-text col col-sm-12 text-center" id="createvm-modal-body">
|
||||
{% trans "Hold tight, we are processing your request" %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer mt-4">
|
||||
<a id="createvm-modal-done-btn" class="btn btn-success btn-ok btn-wide sr-only sr-only-focusable" href="{% url 'matrix:order_success' %}">{% trans "OK" %}</a>
|
||||
<button id="createvm-modal-close-btn" type="button" class="btn btn-danger btn-ok btn-wide sr-only sr-only-focusable" data-dismiss="modal" aria-label="create-vm-close">{% trans "Close" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block js_extra %}
|
||||
|
@ -203,7 +204,4 @@ 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 %}
|
|
@ -7,6 +7,29 @@
|
|||
{% block content %}
|
||||
<div class="container">
|
||||
<!-- Steps Progress bar -->
|
||||
{% if messages %}
|
||||
<div class="row">
|
||||
{% for message in messages %}
|
||||
{% if 'error' in message.tags %}
|
||||
<div class="col-lg-12 alert alert-danger" role="alert">
|
||||
<h4 class="alert-heading">{%trans "Error!" %}</h4>
|
||||
<p class="mb-0">
|
||||
{{ message|safe }}
|
||||
</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-lg-12 alert alert-success" role="alert">
|
||||
<p class="mb-0 float-left">
|
||||
{{ message|safe }}
|
||||
</p>
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="row mt-3 mb-4">
|
||||
<div class="col-lg-12 mx-auto mb-4">
|
||||
<div class="row widget-steps">
|
||||
|
@ -42,27 +65,7 @@
|
|||
{{ details_form.non_field_errors }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-6">
|
||||
<div class="form-group">
|
||||
{{details_form.matrix_domain}}
|
||||
{{ details_form.matrix_domain.errors }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{{details_form.homeserver_domain}}
|
||||
{{ details_form.homeserver_domain.errors }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{{details_form.webclient_domain}}
|
||||
{{ details_form.webclient_domain.errors }}
|
||||
</div>
|
||||
<div class="form-check custom-control custom-checkbox">
|
||||
{{details_form.is_open_registration}}
|
||||
<label class="custom-control-label" for="{{ details_form.is_open_registration.id_for_label}}">{% trans "Is Open registration possible?" %}</label>
|
||||
{{ details_form.is_open_registration.errors }}
|
||||
</div>
|
||||
{{details_form.pricing_name.as_hidden}}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-6">
|
||||
<div class="form-group px-n4">
|
||||
<div class="input-group">
|
||||
|
@ -97,6 +100,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{details_form.pricing_name.as_hidden}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -186,17 +190,19 @@
|
|||
<hr class="mt-1 mx-n3">
|
||||
</div>
|
||||
<div class="row align-items-center flex-row">
|
||||
<div class="col col-lg-6">
|
||||
<h4 class="text-3 font-weight-400">{% trans "Total To Pay"%}</h4>
|
||||
<small>
|
||||
({% if matrix_vm_pricing.vat_inclusive %}{%trans "including VAT" %}{% else %}{%trans "excluding VAT" %}{% endif %})
|
||||
</small>
|
||||
<div class="col col-lg-12">
|
||||
<p>{% trans "Setup Fees"%} <span class="float-right">{{matrix_vm_pricing.set_up_fees}} CHF</span></p>
|
||||
<p>{% trans "Recurring Price"%} <span id="recurring_price" class="float-right">{{request.session.pricing.recurring_price}} CHF</span></p>
|
||||
{% if matrix_vm_pricing.discount_amount %}
|
||||
<div class="text-muted"><span class="text-2">Discount {{matrix_vm_pricing.discount_amount}}</span><span class="text-2 p-1">CHF</span></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col col-lg-6">
|
||||
<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>
|
||||
<p>{% trans "Discount"%} <span class="float-right text-danger"> - {{matrix_vm_pricing.discount_amount}} CHF</span></p>
|
||||
{% endif %}
|
||||
<hr>
|
||||
<p class="text-4 font-weight-500">{% trans "Total To Pay"%}
|
||||
<small>
|
||||
({% if matrix_vm_pricing.vat_inclusive %}{%trans "including VAT" %}{% else %}{%trans "excluding VAT" %}{% endif %})
|
||||
</small>
|
||||
<span id="total" class="float-right">{{request.session.pricing.total}} CHF</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="mt-2 mx-n3">
|
||||
|
@ -205,9 +211,9 @@
|
|||
{% with cards_len=cards|length %}
|
||||
<p class="text-muted">
|
||||
{% if cards_len > 0 %}
|
||||
{% blocktrans %}Please select one of the cards that you used before or fill in your credit card information below.{% endblocktrans %}
|
||||
{% blocktrans %}You haven't enough balance in your wallet, Please select one of the cards that you used before or fill in your credit card information below.{% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans %}You haven't any active cards, Please fill in your credit card information below.{% endblocktrans %}
|
||||
{% blocktrans %}You haven't enough balance in your wallet, Please fill in your credit card information below.{% endblocktrans %}
|
||||
{% endif %}
|
||||
</p>
|
||||
<div>
|
||||
|
@ -247,8 +253,11 @@
|
|||
{% endwith %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-muted">
|
||||
{% blocktrans %}Your wallet has enough balance, Press Continue to fill the VM instance settings.{% endblocktrans %}
|
||||
</p>
|
||||
<div class="text-right">
|
||||
<button id="continue-btn" class="btn btn-primary btn-wide" type="submit">{%trans "Checkout" %}</button>
|
||||
<button id="continue-btn" class="btn btn-primary btn-wide" type="submit">{%trans "Continue" %}</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
|
@ -39,9 +39,9 @@
|
|||
<div class="my-4">
|
||||
<p class="text-success text-20 line-height-07"><i class="fas fa-check-circle"></i></p>
|
||||
<p class="text-success text-8 font-weight-500 line-height-07">{%trans "Success!" %}</p>
|
||||
<p class="lead">{%trans "Order has been successfully added" %}</p>
|
||||
<p class="lead">{%trans "Order has been successfully added. Your VM will be up and running in a few moments. " %}</p>
|
||||
</div>
|
||||
<p class="text-3 mb-4">{%trans "Your current balance is" %}<span class="text-4 font-weight-500"> {{balance}} CHF. </span>{%trans "See transaction details under" %} <a class="btn-link" href="{% url 'matrix:payments' %}">{%trans "Payments" %}</a>.</p>
|
||||
<p class="text-3 mb-4">{%trans "We will send you a confirmation email as soon as it is ready." %} {%trans "Go to your dashboard" %} <a class="btn-link" href="{% url 'matrix:billing' %}">{%trans "Billing" %}</a>.</p>
|
||||
<a href="{% url 'matrix:invoice_download' %}" class="btn btn-white border border-success text-success" role="button"><i class="fas fa-print"></i> {%trans "Download Invoice" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
147
matrixhosting/templates/matrixhosting/orders.html
Normal file
147
matrixhosting/templates/matrixhosting/orders.html
Normal file
|
@ -0,0 +1,147 @@
|
|||
{% extends "matrixhosting/base.html" %}
|
||||
|
||||
{% load static i18n compress %}
|
||||
|
||||
{% block title %} Payments {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Page Content -->
|
||||
{% csrf_token %}
|
||||
<div class="container">
|
||||
<div class="row p-1 mt-4">
|
||||
<div class="col-lg-12 bg-white shadow-sm border border-light rounded py-4 mb-4">
|
||||
<h3 class="text-5 font-weight-400 d-flex align-items-center px-1 mb-4">{% trans "Orders"%}</h3>
|
||||
<!-- Title
|
||||
=============================== -->
|
||||
<div class="transaction-title py-2 px-1">
|
||||
<div class="row">
|
||||
<div class="col-1 col-sm-1 text-center"><span class="">{% trans "ID"%}</span></div>
|
||||
<div class="col-2 col-sm-2 text-center"><span class="">{% trans "Date"%}</span></div>
|
||||
<div class="col-3 col-sm-3">{% trans "Description" %}</div>
|
||||
<div class="col-1 col-sm-1 d-none d-sm-block text-center">{% trans "OneTime Price"%}</div>
|
||||
<div class="col-1 col-sm-1 text-right">{% trans "Recurring Price"%}</div>
|
||||
<div class="col-1 col-sm-1 text-center"><span class="">{% trans "Currency"%}</span></div>
|
||||
<div class="col-2 col-sm-2 text-center">{% trans "End Date"%}</div>
|
||||
<div class="col-1 col-sm-1 text-center" >{% trans "Active"%}</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Title End -->
|
||||
|
||||
<!-- Transaction List
|
||||
=============================== -->
|
||||
<div class="transaction-list">
|
||||
{% for order in object_list %}
|
||||
<div class="transaction-item px-1 py-4" data-id={{order.id}}>
|
||||
<div class="row align-items-center flex-row">
|
||||
<div class="col-1 col-sm-1 text-center"> <span class="d-block text-3 font-weight-400">#{{order.id}}</span></div>
|
||||
<div class="col-2 col-sm-2 text-center"> <span class="d-block text-3 font-weight-300">{{order.starting_date|date:"Y-m-d"}}</span></div>
|
||||
<div class="col-3 col-sm-3"> <span class="d-block text-muted text-3">{{order.description}}</span></div>
|
||||
<div class="col-1 col-sm-1 d-none d-sm-block text-right text-2"> <span class=" text-uppercase">{{order.one_time_price}}</span> </div>
|
||||
<div class="col-1 col-sm-1 d-none d-sm-block text-right text-2"> <span class=" text-uppercase">{{order.recurring_price}}</span> </div>
|
||||
<div class="col-1 col-sm-1 text-center text-2"><span class="">{{order.currency}}</span></div>
|
||||
<div class="col-2 col-sm-2 d-none d-sm-block text-center text-2"> <span class=" text-uppercase">{{order.ending_date|date:"Y-m-d"}}</span> </div>
|
||||
<div class="col-1 col-sm-1 text-center">
|
||||
{% if order.is_closed %}
|
||||
<span class="text-danger" data-toggle="tooltip" data-original-title="Closed"><i class="text-danger fas fa-times-circle"></i></span>
|
||||
{% else %}
|
||||
<span class="text-success" data-toggle="tooltip" data-original-title="Active"><i class="fas fa-check-circle"></i></span>
|
||||
{% endif %}
|
||||
<!-- <span class="float-right" data-toggle="tooltip" data-original-title="See Details"><i class="fas fa-bars"></i></span> -->
|
||||
</div>
|
||||
</div>
|
||||
{% if not order.ending_date %}
|
||||
<a href="#" class="cancel-subscription float-right text-1">{% trans "Cancel Subscription"%}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{%endfor%}
|
||||
</div>
|
||||
<!-- Transaction List End -->
|
||||
|
||||
<!-- Pagination will handled later
|
||||
============================================= -->
|
||||
<ul class="pagination justify-content-center mt-4 mb-0" style="display: none;">
|
||||
<li class="page-item disabled"> <a class="page-link" href="#" tabindex="-1"><i class="fas fa-angle-left"></i></a> </li>
|
||||
<li class="page-item"><a class="page-link" href="#">1</a></li>
|
||||
<li class="page-item active"> <a class="page-link" href="#">2 <span class="sr-only">(current)</span></a> </li>
|
||||
<li class="page-item"><a class="page-link" href="#">3</a></li>
|
||||
<li class="page-item d-flex align-content-center flex-wrap text-muted text-5 mx-1">......</li>
|
||||
<li class="page-item"><a class="page-link" href="#">15</a></li>
|
||||
<li class="page-item"> <a class="page-link" href="#"><i class="fas fa-angle-right"></i></a> </li>
|
||||
</ul>
|
||||
<!-- Paginations end -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="modal fade"
|
||||
tabindex="-1"
|
||||
role="dialog"
|
||||
aria-labelledby="mySmallModalLabel"
|
||||
aria-hidden="true"
|
||||
id="mi-modal"
|
||||
>
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="myModalLabel">{% trans "Cancel Subscription"%}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
{% trans "Are you sure that you want to cancel this subscription?."%} </p>
|
||||
<p>
|
||||
{% blocktrans %} The instance will be active till the end date of the last bill and will be deleted
|
||||
after that. {% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" id="modal-btn-yes">
|
||||
{% trans "Yes Cancel" %}
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" id="modal-btn-no">
|
||||
{% trans "Close" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert" role="alert" id="result"></div>
|
||||
<!-- /.banner -->
|
||||
{% endblock %}
|
||||
|
||||
{% block js_extra %}
|
||||
<script type="text/javascript">
|
||||
var modalConfirm = function (callback) {
|
||||
$(".cancel-subscription").on("click", function (event) {
|
||||
$('.selected').removeClass('selected');
|
||||
$(event.target).parent().addClass('selected');
|
||||
$("#mi-modal").modal("show");
|
||||
});
|
||||
|
||||
$("#modal-btn-yes").on("click", function () {
|
||||
callback(true);
|
||||
});
|
||||
|
||||
$("#modal-btn-no").on("click", function () {
|
||||
callback(false);
|
||||
$("#mi-modal").modal("hide");
|
||||
});
|
||||
};
|
||||
|
||||
modalConfirm(function (confirm) {
|
||||
if (confirm) {
|
||||
var selected_order = $('.selected').data('id');
|
||||
$.ajax({
|
||||
url: '{% url "matrix:orders" %}',
|
||||
type: 'POST',
|
||||
data: {'order_id': selected_order, 'csrfmiddlewaretoken': '{{ csrf_token }}',},
|
||||
success: function (data) {
|
||||
$("#mi-modal").modal("hide");
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -1,6 +1,6 @@
|
|||
{% extends "matrixhosting/base.html" %}
|
||||
|
||||
{% load static i18n %}
|
||||
{% load static i18n compress %}
|
||||
|
||||
{% block title %} Payments {% endblock %}
|
||||
|
||||
|
@ -41,15 +41,15 @@
|
|||
================================ -->
|
||||
<div class="col-12 mb-3" id="allFilters">
|
||||
<div class="custom-control custom-radio custom-control-inline">
|
||||
<input type="radio" id="allTransactions" name="allFilters" class="custom-control-input" checked="">
|
||||
<input type="radio" id="allTransactions" name="filter" data-url="{% url 'matrix:billing' %}" class="custom-control-input" value="all" {% if not type %} checked=""{% endif %}>
|
||||
<label class="custom-control-label" for="allTransactions">{% trans "All Transactions"%}</label>
|
||||
</div>
|
||||
<div class="custom-control custom-radio custom-control-inline">
|
||||
<input type="radio" id="withdrawal" name="allFilters" class="custom-control-input">
|
||||
<input type="radio" id="withdrawal" name="filter" class="custom-control-input" data-url="{% url 'matrix:billing' %}?type=withdraw" value="withdraw" {% if type == 'withdraw' %} checked=""{% endif %}>
|
||||
<label class="custom-control-label" for="withdrawal">{% trans "Withdrawal"%}</label>
|
||||
</div>
|
||||
<div class="custom-control custom-radio custom-control-inline">
|
||||
<input type="radio" id="deposit" name="allFilters" class="custom-control-input">
|
||||
<input type="radio" id="deposit" name="filter" class="custom-control-input" data-url="{% url 'matrix:billing' %}?type=deposit" value="deposit" {% if type == 'deposit' %} checked=""{% endif %}>
|
||||
<label class="custom-control-label" for="deposit">{% trans "Deposit"%}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -84,7 +84,7 @@
|
|||
<div class="transaction-item px-4 py-3" data-id={{payment.id}}>
|
||||
<div class="row align-items-center flex-row">
|
||||
<div class="col-1 col-sm-1 text-center"> <span class="d-block text-3 font-weight-400">#{{payment.id}}</span></div>
|
||||
<div class="col-2 col-sm-2 text-center"> <span class="d-block text-3 font-weight-300">{{payment.timestamp|date:"M d, Y"}}</span> <span class="d-block text-1 font-weight-300 text-uppercase">{{payment.timestamp|date:"H:i"}}</span> </div>
|
||||
<div class="col-2 col-sm-2 text-center"> <span class="d-block text-3 font-weight-300">{{payment.timestamp|date:"Y-m-d"}}</span> <span class="d-block text-1 font-weight-300 text-uppercase">{{payment.timestamp|date:"H:i"}}</span> </div>
|
||||
<div class="col col-sm-4"> <span class="d-block text-muted text-3">{{payment.notes}}</span></div>
|
||||
<div class="col-auto col-sm-2 d-none d-sm-block text-center text-2"> <span class=" text-uppercase">{{payment.type}}</span> </div>
|
||||
<div class="col-3 col-sm-3 text-right text-3"> <span class="text-nowrap">{% if payment.type == 'withdraw' %}- {%else%}+ {%endif%}{{payment.amount}}</span> <span class="text-1 text-uppercase">({{payment.currency}})</span> </div>
|
||||
|
@ -121,4 +121,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block js_extra %}
|
||||
{% compress js %}
|
||||
<script type="text/javascript" src="{% static 'matrixhosting/js/table.js' %}"></script>
|
||||
{% endcompress %}
|
||||
{% endblock js_extra %}
|
|
@ -11,7 +11,7 @@ urlpatterns = [
|
|||
path('order/confirm/', OrderDetailsView.as_view(), name='order_confirmation'),
|
||||
path('order/success/', OrderSuccessView.as_view(), name='order_success'),
|
||||
path('order/invoice/download', InvoiceDownloadView.as_view(), name='invoice_download'),
|
||||
path('payments/', PaymentsView.as_view(), name='payments'),
|
||||
path('dashboard/', Dashboard.as_view(), name='dashboard'),
|
||||
path('billing/', PaymentsView.as_view(), name='billing'),
|
||||
path('orders/', OrdersView.as_view(), name='orders'),
|
||||
path('', IndexView.as_view(), name='index'),
|
||||
]
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import json
|
||||
from uncloud_pay.models import Product, Order, ProductToRecurringPeriod
|
||||
|
||||
|
||||
def finalize_order(request, customer, billing_address,
|
||||
one_time_price, pricing_plan,
|
||||
specs):
|
||||
product = Product.objects.first()
|
||||
recurring_period_product = ProductToRecurringPeriod.objects.filter(product=product, is_default=True).first()
|
||||
order = Order.objects.create(
|
||||
owner=request.user,
|
||||
customer=customer,
|
||||
billing_address=billing_address,
|
||||
one_time_price=one_time_price,
|
||||
pricing_plan=pricing_plan,
|
||||
recurring_period= recurring_period_product.recurring_period,
|
||||
product = product,
|
||||
config=json.dumps(specs)
|
||||
)
|
||||
return order
|
|
@ -1,7 +1,7 @@
|
|||
import logging
|
||||
import json
|
||||
import decimal
|
||||
|
||||
from stripe.error import CardError
|
||||
from django.shortcuts import redirect, render
|
||||
from django.contrib import messages
|
||||
from django.utils.translation import get_language, ugettext_lazy as _
|
||||
|
@ -11,7 +11,7 @@ from django.utils.decorators import method_decorator
|
|||
from django.views import View
|
||||
from django.views.generic import FormView, DetailView
|
||||
from django.views.generic.list import ListView
|
||||
from matrixhosting.forms import InitialRequestForm, RequestHostedVMForm, BillingAddressForm
|
||||
from matrixhosting.forms import InitialRequestForm, BillingAddressForm, RequestDomainsNamesForm
|
||||
from django.urls import reverse
|
||||
from django.conf import settings
|
||||
from django.http import (
|
||||
|
@ -79,11 +79,13 @@ class OrderPaymentView(FormView):
|
|||
) if old_active else BillingAddressForm(
|
||||
initial={'active': True, 'owner': self.request.user.id}
|
||||
)
|
||||
details_form = RequestHostedVMForm(
|
||||
details_form = InitialRequestForm(
|
||||
initial=self.request.session.get('order', {})
|
||||
)
|
||||
balance = get_balance_for_user(self.request.user)
|
||||
customer_id = uncloud_stripe.get_customer_id_for(self.request.user)
|
||||
#TODO optimize this part for better performance
|
||||
uncloud_stripe.sync_cards_for_user(self.request.user)
|
||||
cards = uncloud_stripe.get_customer_cards(customer_id)
|
||||
context.update({
|
||||
'matrix_vm_pricing': PricingPlan.get_by_name(self.request.session.get('pricing', {'name': 'unknown'})['name']),
|
||||
|
@ -94,7 +96,6 @@ class OrderPaymentView(FormView):
|
|||
'stripe_key': settings.STRIPE_PUBLIC_KEY,
|
||||
'show_cards': True if balance < self.request.session.get('pricing')['total'] else False,
|
||||
})
|
||||
|
||||
return context
|
||||
|
||||
@cache_control(no_cache=True, must_revalidate=True, no_store=True)
|
||||
|
@ -107,7 +108,7 @@ class OrderPaymentView(FormView):
|
|||
return self.render_to_response(self.get_context_data())
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
details_form = RequestHostedVMForm(request.POST)
|
||||
details_form = InitialRequestForm(request.POST)
|
||||
billing_address_form = BillingAddressForm(request.POST)
|
||||
if not details_form.is_valid() or not billing_address_form.is_valid():
|
||||
context = self.get_context_data()
|
||||
|
@ -142,10 +143,10 @@ class OrderPaymentView(FormView):
|
|||
if 'error' in validate_result and validate_result['error']:
|
||||
messages.add_message(
|
||||
self.request, messages.ERROR, validate_result["error"],
|
||||
extra_tags='vat_error'
|
||||
extra_tags='error'
|
||||
)
|
||||
return HttpResponseRedirect(
|
||||
reverse('matrix:payment') + '#vat_error'
|
||||
reverse('matrix:payment')
|
||||
)
|
||||
self.request.session["vat_validation_status"] = validate_result["status"]
|
||||
specs = details_form.cleaned_data
|
||||
|
@ -157,7 +158,16 @@ class OrderPaymentView(FormView):
|
|||
)
|
||||
amount = get_balance_for_user(self.request.user) - decimal.Decimal(pricing["total"])
|
||||
if (amount < 0):
|
||||
payment_id = Payment.deposit(request.user, abs(amount), source='stripe')
|
||||
try:
|
||||
payment_id = Payment.deposit(request.user, abs(amount), source='stripe')
|
||||
except CardError as e:
|
||||
messages.add_message(
|
||||
self.request, messages.ERROR, e.user_message,
|
||||
extra_tags='error'
|
||||
)
|
||||
return HttpResponseRedirect(
|
||||
reverse('matrix:payment')
|
||||
)
|
||||
self.request.session['pricing'] = pricing
|
||||
self.request.session['order'] = specs
|
||||
self.request.session['vat_validation_status'] = vat_validation_status
|
||||
|
@ -172,64 +182,50 @@ class OrderDetailsView(DetailView):
|
|||
def dispatch(self, *args, **kwargs):
|
||||
return super().dispatch(*args, **kwargs)
|
||||
|
||||
@cache_control(no_cache=True, must_revalidate=True, no_store=True)
|
||||
def get(self, request, *args, **kwargs):
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'order': self.request.session.get('order'),
|
||||
'pricing': self.request.session.get('pricing'),
|
||||
'balance': get_balance_for_user(self.request.user)
|
||||
}
|
||||
return context
|
||||
|
||||
@cache_control(no_cache=True, must_revalidate=True, no_store=True)
|
||||
def get(self, request, *args, **kwargs):
|
||||
context = self.get_context_data()
|
||||
context['domains_form'] = RequestDomainsNamesForm(initial={})
|
||||
if ('order' not in request.session):
|
||||
return HttpResponseRedirect(reverse('matrix:index'))
|
||||
elif 'pricing' not in self.request.session or 'vat_validation_status' not in self.request.session:
|
||||
return HttpResponseRedirect(reverse('matrix:payment'))
|
||||
return HttpResponseRedirect(reverse('matrix:payment'))
|
||||
return render(request, self.template_name, context)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
customer = StripeCustomer.objects.get(owner=self.request.user)
|
||||
billing_address = BillingAddress.objects.get(id=request.session.get('billing_address_id'))
|
||||
total = self.request.session['pricing']['total']
|
||||
order = finalize_order(request, customer,
|
||||
billing_address,
|
||||
total,
|
||||
PricingPlan.get_by_name(self.request.session['pricing']['name']),
|
||||
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
|
||||
bill.close()
|
||||
response = {
|
||||
'status': True,
|
||||
'redirect': (reverse('matrix:dashboard')),
|
||||
'msg_title': str(_('Thank you for the order.')),
|
||||
'msg_body': str(
|
||||
_('Your VM will be up and running in a few moments.'
|
||||
' We will send you a confirmation email as soon as'
|
||||
' it is ready.'))
|
||||
}
|
||||
|
||||
return JsonResponse(response)
|
||||
|
||||
|
||||
def finalize_order(request, customer, billing_address,
|
||||
one_time_price, pricing_plan,
|
||||
specs):
|
||||
product = Product.objects.first()
|
||||
recurring_period_product = ProductToRecurringPeriod.objects.filter(product=product, is_default=True).first()
|
||||
order = Order.objects.create(
|
||||
owner=request.user,
|
||||
customer=customer,
|
||||
billing_address=billing_address,
|
||||
one_time_price=one_time_price,
|
||||
pricing_plan=pricing_plan,
|
||||
recurring_period= recurring_period_product.recurring_period,
|
||||
product = product,
|
||||
config=json.dumps(specs)
|
||||
)
|
||||
return order
|
||||
domains_form = RequestDomainsNamesForm(self.request.POST)
|
||||
if domains_form.is_valid():
|
||||
customer = StripeCustomer.objects.get(owner=self.request.user)
|
||||
billing_address = BillingAddress.objects.get(id=request.session.get('billing_address_id'))
|
||||
total = self.request.session['pricing']['total']
|
||||
self.request.session['order']['matrix_domain'] = 'ungleich.ch'
|
||||
self.request.session['order']['homeserver_domain'] = domains_form.cleaned_data.get('homeserver_name') + ".matrix.ungleich.cloud"
|
||||
self.request.session['order']['webclient_domain'] = domains_form.cleaned_data.get('webclient_name') + ".matrix.0co2.cloud"
|
||||
self.request.session['order']['is_open_registration'] = domains_form.cleaned_data.get('is_open_registration')
|
||||
order = finalize_order(request, customer,
|
||||
billing_address,
|
||||
total,
|
||||
PricingPlan.get_by_name(self.request.session['pricing']['name']),
|
||||
request.session.get('order'))
|
||||
if order:
|
||||
bill = Bill.create_next_bill_for_order(order)
|
||||
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
|
||||
bill.close()
|
||||
return HttpResponseRedirect(reverse('matrix:order_success'))
|
||||
context = self.get_context_data()
|
||||
context['domains_form'] = domains_form
|
||||
return self.render_to_response(context)
|
||||
|
||||
class OrderSuccessView(DetailView):
|
||||
template_name = "matrixhosting/order_success.html"
|
||||
|
@ -263,27 +259,22 @@ class InvoiceDownloadView(View):
|
|||
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['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
|
||||
}
|
||||
cmd_options = settings.REPORT_FORMAT
|
||||
return PDFTemplateResponse(request=request,
|
||||
template=self.template,
|
||||
filename = self.filename,
|
||||
cmd_options= cmd_options,
|
||||
footer_template= 'matrixhosting/includes/invoice_footer.html',
|
||||
context= self.get_context_data())
|
||||
|
||||
|
||||
class Dashboard(ListView):
|
||||
template_name = "matrixhosting/dashboard.html"
|
||||
class OrdersView(ListView):
|
||||
template_name = "matrixhosting/orders.html"
|
||||
model = Order
|
||||
|
||||
@method_decorator(login_required)
|
||||
|
@ -318,10 +309,13 @@ class PaymentsView(ListView):
|
|||
def get_context_data(self, **kwargs):
|
||||
context = super(PaymentsView, self).get_context_data(**kwargs)
|
||||
context.update({
|
||||
'balance': get_balance_for_user(self.request.user)
|
||||
'balance': get_balance_for_user(self.request.user),
|
||||
'type': self.request.GET.get('type')
|
||||
})
|
||||
return context
|
||||
|
||||
def get_queryset(self):
|
||||
if self.request.GET.get('type'):
|
||||
return Payment.objects.filter(owner=self.request.user, type=self.request.GET.get('type'))
|
||||
return Payment.objects.filter(owner=self.request.user)
|
||||
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ name }}-matrix
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{ name }}-matrix
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: {{ name }}-matrix
|
||||
use-as-service: {{ name }}
|
|
@ -11,6 +11,8 @@ EMAIL_HOST=
|
|||
EMAIL_HOST_USER=
|
||||
EMAIL_HOST_PASSWORD=
|
||||
GITLAB_SERVER=
|
||||
GITLAB_YAML_DIR=
|
||||
GITLAB_PROJECT_ID=
|
||||
GITLAB_OAUTH_TOKEN=
|
||||
GITLAB_AUTHOR_EMAIL=
|
||||
GITLAB_AUTHOR_NAME=
|
|
@ -202,10 +202,10 @@ COMPRESS_ENABLED = True
|
|||
#VM Deployment TEMPLATE
|
||||
GITLAB_SERVER = env('GITLAB_SERVER')
|
||||
GITLAB_OAUTH_TOKEN = env('GITLAB_OAUTH_TOKEN')
|
||||
GITLAB_PROJECT_ID = 388
|
||||
GITLAB_PROJECT_ID = env('GITLAB_PROJECT_ID')
|
||||
GITLAB_AUTHOR_EMAIL = env('GITLAB_AUTHOR_EMAIL')
|
||||
GITLAB_AUTHOR_NAME = env('GITLAB_AUTHOR_NAME')
|
||||
GITLAB_YAML_DIR = ''
|
||||
GITLAB_YAML_DIR = env('GITLAB_YAML_DIR')
|
||||
|
||||
# XML-RPC interface of opennebula
|
||||
OPENNEBULA_URL = 'https://opennebula.example.com:2634/RPC2'
|
||||
|
@ -270,6 +270,15 @@ Q_CLUSTER = {
|
|||
'db': 0, }
|
||||
}
|
||||
|
||||
REPORT_FORMAT = {
|
||||
'page_height': 200,
|
||||
'page_width':175,
|
||||
'orientation': 'Portrait',
|
||||
'header_spacing': 65,
|
||||
'margin_bottom':25,
|
||||
'header_line': False,
|
||||
}
|
||||
|
||||
# Overwrite settings with local settings, if existing
|
||||
try:
|
||||
from uncloud.local_settings import *
|
||||
|
|
|
@ -50,7 +50,7 @@ class BillAdmin(admin.ModelAdmin):
|
|||
"uncloud_pay/bill.html.j2",
|
||||
{
|
||||
'bill': bill,
|
||||
'bill_records': bill.billrecord_set.all()
|
||||
'bill_records': bill.bill_records.all()
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -68,12 +68,12 @@ class BillAdmin(admin.ModelAdmin):
|
|||
|
||||
return render(request, 'uncloud_pay/bill.html.j2',
|
||||
{'bill': bill,
|
||||
'bill_records': bill.billrecord_set.all()
|
||||
'bill_records': bill.bill_records.all()
|
||||
})
|
||||
|
||||
|
||||
bill_html = render_to_string("bill.html.j2", {'bill': bill,
|
||||
'bill_records': bill.billrecord_set.all()
|
||||
'bill_records': bill.bill_records.all()
|
||||
})
|
||||
|
||||
bytestring_to_pdf(bill_html.encode('utf-8'), output_file)
|
||||
|
|
|
@ -122,7 +122,7 @@ class Payment(models.Model):
|
|||
|
||||
@classmethod
|
||||
def withdraw(cls, owner, amount, currency='CHF', notes=''):
|
||||
cls.objects.create(owner=owner, type="withdraw", amount=amount,
|
||||
return cls.objects.create(owner=owner, type="withdraw", amount=amount,
|
||||
currency=currency, notes=notes)
|
||||
|
||||
|
||||
|
@ -753,7 +753,7 @@ class Order(models.Model):
|
|||
self.ending_date = timezone.now()
|
||||
self.should_be_billed = False
|
||||
self.save()
|
||||
if self.instance_id:
|
||||
if hasattr(self, 'instance_id'):
|
||||
last_bill_record = BillRecord.objects.filter(order=self).order_by('id').last()
|
||||
schedule('matrixhosting.tasks.delete_instance',
|
||||
self.instance_id,
|
||||
|
@ -790,13 +790,6 @@ class Order(models.Model):
|
|||
else:
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_closed(self):
|
||||
if self.all_usage_billed and self.ending_date:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_recurring(self):
|
||||
return self.recurring_price > 0
|
||||
|
@ -833,7 +826,6 @@ class Order(models.Model):
|
|||
|
||||
new_order = self.__class__(owner=self.owner,
|
||||
billing_address=self.billing_address,
|
||||
description=self.description,
|
||||
product=self.product,
|
||||
config=config,
|
||||
pricing_plan=self.pricing_plan,
|
||||
|
@ -934,7 +926,7 @@ class Order(models.Model):
|
|||
|
||||
vat_rate = VATRate.get_vat_rate(self.billing_address)
|
||||
vat_validation_status = "verified" if self.billing_address.vat_number_validated_on and self.billing_address.vat_number_verified else False
|
||||
subtotal, subtotal_after_discount, price_after_discount_with_vat, vat, vat_percent, discount = uncloud_pay.utils.apply_vat_discount(
|
||||
subtotal, subtotal_after_discount, price_after_discount_with_vat, vat, vat_percent, vat_amount, discount = uncloud_pay.utils.apply_vat_discount(
|
||||
recurring_price, self.pricing_plan,
|
||||
vat_rate=vat_rate * 100, vat_validation_status = vat_validation_status
|
||||
)
|
||||
|
@ -1077,6 +1069,17 @@ class Bill(models.Model):
|
|||
# This Customer Hasn't any active orders
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def create_next_bill_for_order(cls, order, ending_date=None):
|
||||
"""
|
||||
Create the next bill for a specific order of a user
|
||||
"""
|
||||
bill = cls.get_or_create_bill(order.billing_address, ending_date=ending_date)
|
||||
print("??????????????????????????????")
|
||||
print(bill.id)
|
||||
order.create_bill_record(bill)
|
||||
return bill
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_or_create_bill(cls, billing_address, ending_date=None):
|
||||
|
@ -1110,7 +1113,7 @@ class Bill(models.Model):
|
|||
|
||||
if not ending_date:
|
||||
ending_date = end_of_month(starting_date)
|
||||
|
||||
|
||||
if not bill:
|
||||
bill = cls.objects.create(
|
||||
owner=billing_address.owner,
|
||||
|
@ -1192,6 +1195,9 @@ class BillRecord(models.Model):
|
|||
return bill_line
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
|
||||
print(self.ending_date)
|
||||
print(self.starting_date)
|
||||
if self.ending_date < self.starting_date:
|
||||
raise ValidationError("End date cannot be before starting date")
|
||||
|
||||
|
|
|
@ -151,7 +151,7 @@ class OrderTestCase(TestCase):
|
|||
order_config = json.dumps({
|
||||
'cores': 2,
|
||||
'memory':4,
|
||||
'storage': 200
|
||||
'storage': 200,
|
||||
})
|
||||
order1 = Order.objects.create(owner=self.user,
|
||||
billing_address=self.ba,
|
||||
|
@ -470,7 +470,7 @@ class BillTestCase(TestCase):
|
|||
bill = Bill.create_next_bill_for_user_address(self.recurring_user_addr)
|
||||
|
||||
self.assertEqual(order.billrecord_set.count(), 1)
|
||||
self.assertEqual(bill.billrecord_set.count(), 1)
|
||||
self.assertEqual(bill.bill_records.count(), 1)
|
||||
|
||||
|
||||
def test_new_bill_after_closing(self):
|
||||
|
|
|
@ -143,17 +143,15 @@ def get_order_total_with_vat(cores, memory, storage,
|
|||
)
|
||||
)
|
||||
return None
|
||||
|
||||
subtotal = (
|
||||
pricing.set_up_fees +
|
||||
(decimal.Decimal(cores) * pricing.cores_unit_price) +
|
||||
(decimal.Decimal(memory) * pricing.ram_unit_price) +
|
||||
(decimal.Decimal(storage) * (pricing.storage_unit_price))
|
||||
)
|
||||
recurring_price = (decimal.Decimal(cores) * pricing.cores_unit_price) + \
|
||||
(decimal.Decimal(memory) * pricing.ram_unit_price) + \
|
||||
(decimal.Decimal(storage) * (pricing.storage_unit_price))
|
||||
subtotal = pricing.set_up_fees + recurring_price
|
||||
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,
|
||||
'recurring_price': round(float(recurring_price), 2),
|
||||
"subtotal": subtotal,
|
||||
"discount": discount,
|
||||
"vat": vat, "vat_percent": vat_percent,
|
||||
|
|
Loading…
Reference in a new issue