Merge branch 'master' into feature/6561/invoices-webhook
This commit is contained in:
commit
6873d901c9
25 changed files with 892 additions and 182 deletions
|
|
@ -9,7 +9,6 @@ from django.contrib.auth import authenticate
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from membership.models import CustomUser
|
||||
from utils.hosting_utils import get_all_public_keys
|
||||
from .models import UserHostingKey, GenericProduct
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -110,9 +109,14 @@ class ProductPaymentForm(GenericPaymentForm):
|
|||
)
|
||||
)
|
||||
if self.product.product_is_subscription:
|
||||
payment_type = "month"
|
||||
if self.product.product_subscription_interval == "month":
|
||||
payment_type = _('Monthly subscription')
|
||||
elif self.product.product_subscription_interval == "year":
|
||||
payment_type = _('Yearly subscription')
|
||||
self.fields['amount'].label = "{amt} ({payment_type})".format(
|
||||
amt=_('Amount in CHF'),
|
||||
payment_type=_('Monthly subscription')
|
||||
payment_type=payment_type
|
||||
)
|
||||
else:
|
||||
self.fields['amount'].label = "{amt} ({payment_type})".format(
|
||||
|
|
@ -193,15 +197,6 @@ class UserHostingKeyForm(forms.ModelForm):
|
|||
KEY_ERROR_MESSAGE = _("Please input a proper SSH key")
|
||||
openssh_pubkey_str = self.data.get('public_key').strip()
|
||||
|
||||
if openssh_pubkey_str in get_all_public_keys(self.request.user):
|
||||
key_name = UserHostingKey.objects.filter(
|
||||
user_id=self.request.user.id,
|
||||
public_key=openssh_pubkey_str).first().name
|
||||
KEY_EXISTS_MESSAGE = _(
|
||||
"This key exists already with the name \"%(name)s\"") % {
|
||||
'name': key_name}
|
||||
raise forms.ValidationError(KEY_EXISTS_MESSAGE)
|
||||
|
||||
with tempfile.NamedTemporaryFile(delete=True) as tmp_public_key_file:
|
||||
tmp_public_key_file.write(openssh_pubkey_str.encode('utf-8'))
|
||||
tmp_public_key_file.flush()
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-07-09 15:21+0000\n"
|
||||
"POT-Creation-Date: 2019-11-15 16:40+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
|
@ -28,28 +28,31 @@ msgid "User does not exist"
|
|||
msgstr "Der Benutzer existiert nicht"
|
||||
|
||||
msgid "Choose a product"
|
||||
msgstr ""
|
||||
msgstr "Wähle ein Produkt"
|
||||
|
||||
msgid "Amount in CHF"
|
||||
msgstr "Betrag"
|
||||
|
||||
msgid "Recurring monthly"
|
||||
msgstr ""
|
||||
msgstr "monatlich wiederkehrend"
|
||||
|
||||
msgid "Amount field does not match"
|
||||
msgstr ""
|
||||
msgstr "Betragsfeld stimmt nicht überein"
|
||||
|
||||
msgid "Recurring field does not match"
|
||||
msgstr ""
|
||||
msgstr "Betragsfeld stimmt nicht überein"
|
||||
|
||||
msgid "Product name"
|
||||
msgstr "Produkt"
|
||||
|
||||
msgid "Monthly subscription"
|
||||
msgstr ""
|
||||
msgstr "Monatliches Abonnement"
|
||||
|
||||
msgid "Yearly subscription"
|
||||
msgstr "Jährliches Abonnement"
|
||||
|
||||
msgid "One time payment"
|
||||
msgstr ""
|
||||
msgstr "Einmalzahlung"
|
||||
|
||||
msgid "Confirm Password"
|
||||
msgstr "Passwort Bestätigung"
|
||||
|
|
@ -72,12 +75,8 @@ msgstr "Key-Name"
|
|||
msgid "Please input a proper SSH key"
|
||||
msgstr "Bitte verwende einen gültigen SSH-Key"
|
||||
|
||||
#, python-format
|
||||
msgid "This key exists already with the name \"%(name)s\""
|
||||
msgstr "Der SSH-Key mit dem Name \"%(name)s\" existiert bereits"
|
||||
|
||||
msgid "Comma not accepted in the name of the key"
|
||||
msgstr ""
|
||||
msgstr "Komma im Namen des Keys wird nicht akzeptiert"
|
||||
|
||||
msgid "All Rights Reserved"
|
||||
msgstr "Alle Rechte vorbehalten"
|
||||
|
|
@ -242,6 +241,10 @@ msgstr ""
|
|||
msgid "You can view your VM detail by clicking the button below."
|
||||
msgstr "Um die Rechnung zu sehen, klicke auf den Button unten."
|
||||
|
||||
msgid "You can log in to your VM by the username <strong>puffy</strong>."
|
||||
msgstr ""
|
||||
"Du kannst Dich auf Deiner VM mit dem user <strong>puffy</strong> einloggen."
|
||||
|
||||
msgid "View Detail"
|
||||
msgstr "Details anzeigen"
|
||||
|
||||
|
|
@ -255,6 +258,9 @@ msgstr "Deine Bestellung von %(vm_name)s wurde entgegengenommen."
|
|||
msgid "You can view your VM detail by following the link below."
|
||||
msgstr "Um die Rechnung zu sehen, klicke auf den Link unten."
|
||||
|
||||
msgid "You can log in to your VM by the username puffy."
|
||||
msgstr "Du kannst Dich auf Deiner VM mit dem user puffy einloggen."
|
||||
|
||||
msgid "Password Reset"
|
||||
msgstr "Passwort zurücksetzen"
|
||||
|
||||
|
|
@ -398,13 +404,19 @@ msgid "Amount"
|
|||
msgstr "Betrag"
|
||||
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
msgstr "Beschreibung"
|
||||
|
||||
msgid "Recurring"
|
||||
msgstr ""
|
||||
msgstr "wiederkehrend"
|
||||
|
||||
msgid "of"
|
||||
msgstr "von"
|
||||
|
||||
msgid "each year"
|
||||
msgstr "jedes Jahr"
|
||||
|
||||
msgid "of every month"
|
||||
msgstr ""
|
||||
msgstr "jeden Monat"
|
||||
|
||||
msgid "BACK TO LIST"
|
||||
msgstr "ZURÜCK ZUR LISTE"
|
||||
|
|
@ -416,16 +428,13 @@ msgid "VM ID"
|
|||
msgstr ""
|
||||
|
||||
msgid "IP Address"
|
||||
msgstr ""
|
||||
msgstr "IP-Adresse"
|
||||
|
||||
msgid "See Invoice"
|
||||
msgstr "Siehe Rechnung"
|
||||
|
||||
msgid "Page"
|
||||
msgstr ""
|
||||
|
||||
msgid "of"
|
||||
msgstr ""
|
||||
msgstr "Seite"
|
||||
|
||||
msgid "Log in"
|
||||
msgstr "Anmelden"
|
||||
|
|
@ -487,7 +496,7 @@ msgid "Hold tight, we are processing your request"
|
|||
msgstr "Bitte warten - wir bearbeiten Deine Anfrage gerade"
|
||||
|
||||
msgid "OK"
|
||||
msgstr ""
|
||||
msgstr "Ok"
|
||||
|
||||
msgid "Close"
|
||||
msgstr "Schliessen"
|
||||
|
|
@ -843,7 +852,7 @@ msgstr "Ungültige Speicher-Grösse"
|
|||
|
||||
#, python-brace-format
|
||||
msgid "Incorrect pricing name. Please contact support{support_email}"
|
||||
msgstr ""
|
||||
msgstr "Ungültige Preisbezeichnung. Bitte kontaktiere den Support{support_email}"
|
||||
|
||||
msgid ""
|
||||
"We could not find the requested VM. Please "
|
||||
|
|
@ -862,7 +871,7 @@ msgstr "Fehler beenden VM"
|
|||
msgid ""
|
||||
"VM terminate action timed out. Please contact support@datacenterlight.ch for "
|
||||
"further information."
|
||||
msgstr ""
|
||||
msgstr "VM beendet wegen Zeitüberschreitung. Bitte kontaktiere support@datacenterlight.ch für weitere Informationen."
|
||||
|
||||
#, python-format
|
||||
msgid "Virtual Machine %(vm_name)s Cancelled"
|
||||
|
|
@ -873,6 +882,10 @@ msgstr ""
|
|||
"Es gab einen Fehler bei der Bearbeitung Deine Anfrage. Bitte versuche es "
|
||||
"noch einmal."
|
||||
|
||||
#, python-format
|
||||
#~ msgid "This key exists already with the name \"%(name)s\""
|
||||
#~ msgstr "Der SSH-Key mit dem Name \"%(name)s\" existiert bereits"
|
||||
|
||||
#~ msgid "Add your public SSH key"
|
||||
#~ msgstr "Füge deinen öffentlichen SSH-Key hinzu"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import datetime
|
||||
import logging
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
|
@ -19,6 +20,10 @@ class Command(BaseCommand):
|
|||
def handle(self, *args, **options):
|
||||
try:
|
||||
for email in options['customer_email']:
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
"---------------------------------------------")
|
||||
)
|
||||
stripe_utils = StripeUtils()
|
||||
user = CustomUser.objects.get(email=email)
|
||||
if hasattr(user, 'stripecustomer'):
|
||||
|
|
@ -39,7 +44,9 @@ class Command(BaseCommand):
|
|||
)
|
||||
if all_invoices_response['error'] is not None:
|
||||
self.stdout.write(self.style.ERROR(all_invoices_response['error']))
|
||||
exit(1)
|
||||
user.import_stripe_bill_remark += "{}: {},".format(datetime.datetime.now(), all_invoices_response['error'])
|
||||
user.save()
|
||||
continue
|
||||
all_invoices = all_invoices_response['response_object']
|
||||
self.stdout.write(self.style.SUCCESS("Obtained {} invoices".format(len(all_invoices) if all_invoices is not None else 0)))
|
||||
num_invoice_created = 0
|
||||
|
|
@ -50,12 +57,25 @@ class Command(BaseCommand):
|
|||
logger.debug("Invoice %s exists already. Not importing." % invoice['invoice_id'])
|
||||
except MonthlyHostingBill.DoesNotExist as dne:
|
||||
logger.debug("Invoice id %s does not exist" % invoice['invoice_id'])
|
||||
num_invoice_created += 1 if MonthlyHostingBill.create(invoice) is not None else logger.error("Did not import invoice for %s" % str(invoice))
|
||||
|
||||
if MonthlyHostingBill.create(invoice) is not None:
|
||||
num_invoice_created += 1
|
||||
else:
|
||||
user.import_stripe_bill_remark += "{}: Import failed - {},".format(
|
||||
datetime.datetime.now(),
|
||||
invoice['invoice_id'])
|
||||
user.save()
|
||||
logger.error("Did not import invoice for %s"
|
||||
"" % str(invoice))
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS("Number of invoices imported = %s" % num_invoice_created)
|
||||
)
|
||||
else:
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
'Customer email %s does not have a stripe customer.' % email))
|
||||
user.import_stripe_bill_remark += "{}: No stripecustomer,".format(
|
||||
datetime.datetime.now()
|
||||
)
|
||||
user.save()
|
||||
except Exception as e:
|
||||
print(" *** Error occurred. Details {}".format(str(e)))
|
||||
|
|
|
|||
44
hosting/management/commands/import_vat_rates.py
Normal file
44
hosting/management/commands/import_vat_rates.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
from django.core.management.base import BaseCommand
|
||||
import csv
|
||||
from hosting.models import VATRates
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = '''Imports VAT Rates. Assume vat rates of format https://github.com/kdeldycke/vat-rates/blob/master/vat_rates.csv'''
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('csv_file', nargs='+', type=str)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
try:
|
||||
for c_file in options['csv_file']:
|
||||
print("c_file = %s" % c_file)
|
||||
with open(c_file, mode='r') as csv_file:
|
||||
csv_reader = csv.DictReader(csv_file)
|
||||
line_count = 0
|
||||
for row in csv_reader:
|
||||
if line_count == 0:
|
||||
line_count += 1
|
||||
obj, created = VATRates.objects.get_or_create(
|
||||
start_date=row["start_date"],
|
||||
stop_date=row["stop_date"] if row["stop_date"] is not "" else None,
|
||||
territory_codes=row["territory_codes"],
|
||||
currency_code=row["currency_code"],
|
||||
rate=row["rate"],
|
||||
rate_type=row["rate_type"],
|
||||
description=row["description"]
|
||||
)
|
||||
if created:
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
'%s. %s - %s - %s - %s' % (
|
||||
line_count,
|
||||
obj.start_date,
|
||||
obj.stop_date,
|
||||
obj.territory_codes,
|
||||
obj.rate
|
||||
)
|
||||
))
|
||||
line_count+=1
|
||||
|
||||
except Exception as e:
|
||||
print(" *** Error occurred. Details {}".format(str(e)))
|
||||
20
hosting/migrations/0056_auto_20191026_0454.py
Normal file
20
hosting/migrations/0056_auto_20191026_0454.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.4 on 2019-10-26 04:54
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('hosting', '0055_auto_20190701_1614'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='hostingbilllineitem',
|
||||
name='amount',
|
||||
field=models.IntegerField(),
|
||||
),
|
||||
]
|
||||
30
hosting/migrations/0057_vatrates.py
Normal file
30
hosting/migrations/0057_vatrates.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.4 on 2019-11-15 05:16
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import utils.mixins
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('hosting', '0056_auto_20191026_0454'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='VATRates',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('start_date', models.DateField(blank=True, null=True)),
|
||||
('stop_date', models.DateField(blank=True, null=True)),
|
||||
('territory_codes', models.TextField(blank=True, default='')),
|
||||
('currency_code', models.CharField(max_length=10)),
|
||||
('rate', models.FloatField()),
|
||||
('rate_type', models.TextField(blank=True, default='')),
|
||||
('description', models.TextField(blank=True, default='')),
|
||||
],
|
||||
bases=(utils.mixins.AssignPermissionsMixin, models.Model),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.4 on 2019-11-15 14:57
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('hosting', '0057_vatrates'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='genericproduct',
|
||||
name='product_subscription_interval',
|
||||
field=models.CharField(default='month', help_text='Choose between `year` and `month`', max_length=10),
|
||||
),
|
||||
]
|
||||
|
|
@ -78,13 +78,17 @@ class GenericProduct(AssignPermissionsMixin, models.Model):
|
|||
product_price = models.DecimalField(max_digits=6, decimal_places=2)
|
||||
product_vat = models.DecimalField(max_digits=6, decimal_places=4, default=0)
|
||||
product_is_subscription = models.BooleanField(default=True)
|
||||
product_subscription_interval = models.CharField(
|
||||
max_length=10, default="month",
|
||||
help_text="Choose between `year` and `month`")
|
||||
|
||||
def __str__(self):
|
||||
return self.product_name
|
||||
|
||||
def get_actual_price(self):
|
||||
def get_actual_price(self, vat_rate=None):
|
||||
VAT = vat_rate if vat_rate is not None else self.product_vat
|
||||
return round(
|
||||
self.product_price + (self.product_price * self.product_vat), 2
|
||||
float(self.product_price) + float(self.product_price) * float(VAT), 2
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -322,7 +326,10 @@ class MonthlyHostingBill(AssignPermissionsMixin, models.Model):
|
|||
logger.debug("Neither subscription id nor vm_id available")
|
||||
logger.debug("Can't import invoice")
|
||||
return None
|
||||
|
||||
if args['order'] is None:
|
||||
logger.error(
|
||||
"Order is None for {}".format(args['invoice_id']))
|
||||
return None
|
||||
instance = cls.objects.create(
|
||||
created=datetime.utcfromtimestamp(
|
||||
args['created']).replace(tzinfo=pytz.utc),
|
||||
|
|
@ -340,7 +347,10 @@ class MonthlyHostingBill(AssignPermissionsMixin, models.Model):
|
|||
args['period_start']).replace(tzinfo=pytz.utc),
|
||||
period_end=datetime.utcfromtimestamp(
|
||||
args['period_end']).replace(tzinfo=pytz.utc),
|
||||
billing_reason=args['billing_reason'],
|
||||
billing_reason=(
|
||||
args['billing_reason']
|
||||
if args['billing_reason'] is not None else ''
|
||||
),
|
||||
discount=args['discount'],
|
||||
total=args['total'],
|
||||
lines_data_count=args['lines_data_count'],
|
||||
|
|
@ -469,7 +479,7 @@ class HostingBillLineItem(AssignPermissionsMixin, models.Model):
|
|||
on_delete=models.CASCADE)
|
||||
stripe_plan = models.ForeignKey(StripePlan, null=True,
|
||||
on_delete=models.CASCADE)
|
||||
amount = models.PositiveSmallIntegerField()
|
||||
amount = models.IntegerField()
|
||||
description = models.CharField(max_length=255)
|
||||
discountable = models.BooleanField()
|
||||
metadata = models.CharField(max_length=128)
|
||||
|
|
@ -722,6 +732,16 @@ class UserCardDetail(AssignPermissionsMixin, models.Model):
|
|||
return None
|
||||
|
||||
|
||||
class VATRates(AssignPermissionsMixin, models.Model):
|
||||
start_date = models.DateField(blank=True, null=True)
|
||||
stop_date = models.DateField(blank=True, null=True)
|
||||
territory_codes = models.TextField(blank=True, default='')
|
||||
currency_code = models.CharField(max_length=10)
|
||||
rate = models.FloatField()
|
||||
rate_type = models.TextField(blank=True, default='')
|
||||
description = models.TextField(blank=True, default='')
|
||||
|
||||
|
||||
class FailedInvoice(AssignPermissionsMixin, models.Model):
|
||||
permissions = ('view_failedinvoice',)
|
||||
stripe_customer = models.ForeignKey(StripeCustomer)
|
||||
|
|
|
|||
|
|
@ -33,6 +33,11 @@
|
|||
<p style="line-height: 1.75; font-family: Lato, Arial, sans-serif; font-weight: 300; margin: 0;">
|
||||
{% blocktrans %}You can view your VM detail by clicking the button below.{% endblocktrans %}
|
||||
</p>
|
||||
{% if 'OpenBSD' in vm_name %}
|
||||
<p style="line-height: 1.75; font-family: Lato, Arial, sans-serif; font-weight: 300; margin: 0;">
|
||||
{% blocktrans %}You can log in to your VM by the username <strong>puffy</strong>.{% endblocktrans %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@
|
|||
{% blocktrans %}You have ordered a new virtual machine!{% endblocktrans %}
|
||||
{% blocktrans %}Your order of {{vm_name}} has been charged.{% endblocktrans %}
|
||||
{% blocktrans %}You can view your VM detail by following the link below.{% endblocktrans %}
|
||||
{% if 'OpenBSD' in vm_name %}
|
||||
{% blocktrans %}You can log in to your VM by the username puffy.{% endblocktrans %}
|
||||
{% endif %}
|
||||
|
||||
{{ base_url }}{{ order_url }}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@
|
|||
<label for="configuration">Configuration: </label>
|
||||
<select class="form-control" name="vm_template_id" id="{{vm.hosting_company}}-configuration" data-vm-type="{{vm.hosting_company}}">
|
||||
{% for template in templates %}
|
||||
<option value="{{template.id}}">{{ template.name }}</option>
|
||||
<option value="{{template.id}}">{{ template.name|cut:'public-' }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -93,10 +93,10 @@
|
|||
<table>
|
||||
<tr><th style="width: 35%">Product</th><th style="width: 20%">Period</th><th style="text-align: center; width: 10%">Qty</th><th align="center" style="width: 10%; text-align: center;">Unit Price</th><th style="width: 10%; text-align: right;">Total</th></tr>
|
||||
{% for line_item in line_items %}
|
||||
<tr class="border_bottom"><td>{% if line_item.description|length > 0 %}{{line_item.description}}{% elif line_item.stripe_plan.stripe_plan_name|length > 0 %}{{line_item.stripe_plan.stripe_plan_name}}{% else %}{{line_item.get_item_detail_str|safe}}{% endif %}</td><td>{{ line_item.period_start | date:'Y-m-d' }} — {{ line_item.period_end | date:'Y-m-d' }}</td><td align="center">{{line_item.quantity}}</td><td align="center">{{line_item.unit_amount_in_chf}}</td><td align="right">{{line_item.amount_in_chf}}</td></tr>
|
||||
<tr class="border_bottom"><td>{% if line_item.description|length > 0 %}{{line_item.description}}{% elif line_item.stripe_plan.stripe_plan_name|length > 0 %}{{line_item.stripe_plan.stripe_plan_name}}{% else %}{{line_item.get_item_detail_str|safe}}{% endif %}</td><td>{{ line_item.period_start | date:'Y-m-d' }} — {{ line_item.period_end | date:'Y-m-d' }}</td><td align="center">{{line_item.quantity}}</td><td align="center">{{line_item.unit_amount_in_chf}}</td><td align="right">{{line_item.amount_in_chf|floatformat:2}}</td></tr>
|
||||
|
||||
{% endfor %}
|
||||
<tr class="grand-total-padding"><td colspan="4">Grand Total</td><td align="right">{{total_in_chf}}</td></tr>
|
||||
<tr class="grand-total-padding"><td colspan="4">Grand Total</td><td align="right">{{total_in_chf|floatformat:2}}</td></tr>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>
|
||||
|
|
@ -195,8 +195,13 @@
|
|||
{% if invoice.order.subscription_id %}
|
||||
<p>
|
||||
<span>{% trans "Recurring" %}: </span>
|
||||
<strong class="pull-right">{{invoice.order.created_at|date:'d'|ordinal}}
|
||||
{% if invoice.order.generic_product.product_subscription_interval == 'year' %}
|
||||
<strong class="pull-right">{{invoice.order.created_at|date:'d'|ordinal}} {% trans "of" %} {{invoice.order.created_at|date:'b'|title}}
|
||||
{% trans "each year" %}</strong>
|
||||
{% else %}
|
||||
<strong class="pull-right">{{invoice.order.created_at|date:'d'|ordinal}}
|
||||
{% trans "of every month" %}</strong>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -186,7 +186,13 @@
|
|||
{% if order.subscription_id %}
|
||||
<p>
|
||||
<span>{% trans "Recurring" %}: </span>
|
||||
<strong class="pull-right">{{order.created_at|date:'d'|ordinal}} {% trans "of every month" %}</strong>
|
||||
{% if order.generic_product.product_subscription_interval == 'year' %}
|
||||
<strong class="pull-right">{{order.created_at|date:'d'|ordinal}} {% trans "of" %} {{order.created_at|date:'b'|title}}
|
||||
{% trans "each year" %}</strong>
|
||||
{% else %}
|
||||
<strong class="pull-right">{{order.created_at|date:'d'|ordinal}}
|
||||
{% trans "of every month" %}</strong>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1282,6 +1282,10 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView):
|
|||
context['vm']['total_price'] = (
|
||||
price + vat - discount['amount']
|
||||
)
|
||||
except TypeError:
|
||||
logger.error("Type error. Probably we "
|
||||
"came from a generic product. "
|
||||
"Invoice ID %s" % obj.invoice_id)
|
||||
except WrongIdError:
|
||||
logger.error("WrongIdError while accessing "
|
||||
"invoice {}".format(obj.invoice_id))
|
||||
|
|
@ -1587,6 +1591,7 @@ class VirtualMachineView(LoginRequiredMixin, View):
|
|||
|
||||
# Cancel Stripe subscription
|
||||
stripe_utils = StripeUtils()
|
||||
hosting_order = None
|
||||
try:
|
||||
hosting_order = HostingOrder.objects.get(
|
||||
vm_id=vm.id
|
||||
|
|
@ -1648,9 +1653,10 @@ class VirtualMachineView(LoginRequiredMixin, View):
|
|||
else:
|
||||
sleep(2)
|
||||
if not response['status']:
|
||||
response['text'] = _("VM terminate action timed out. Please "
|
||||
"contact support@datacenterlight.ch for "
|
||||
"further information.")
|
||||
response['text'] = str(_("VM terminate action timed out. "
|
||||
"Please contact "
|
||||
"support@datacenterlight.ch for "
|
||||
"further information."))
|
||||
context = {
|
||||
'vm_name': vm_name,
|
||||
'base_url': "{0}://{1}".format(
|
||||
|
|
@ -1671,6 +1677,11 @@ class VirtualMachineView(LoginRequiredMixin, View):
|
|||
email = BaseEmail(**email_data)
|
||||
email.send()
|
||||
admin_email_body.update(response)
|
||||
admin_email_body["customer_email"] = owner.email
|
||||
admin_email_body["VM_ID"] = vm.id
|
||||
admin_email_body["VM_created_at"] = (str(hosting_order.created_at) if
|
||||
hosting_order is not None
|
||||
else "unknown")
|
||||
admin_msg_sub = "VM and Subscription for VM {} and user: {}".format(
|
||||
vm.id,
|
||||
owner.email
|
||||
|
|
@ -1760,7 +1771,8 @@ class CheckUserVM(APIView):
|
|||
response = check_otp(user, realm, token)
|
||||
if response != 200:
|
||||
return Response('Invalid token', 403)
|
||||
manager = OpenNebulaManager()
|
||||
manager = OpenNebulaManager(settings.OPENNEBULA_USERNAME,
|
||||
settings.OPENNEBULA_PASSWORD)
|
||||
# not the best way to lookup vms by ip
|
||||
# TODO: make this optimal
|
||||
vms = manager.get_vms()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue