Compare commits
45 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49ef761b2e | ||
|
|
f82ed81b33 | ||
|
|
1ff577ddcd | ||
|
|
15ef20dbc1 | ||
|
|
dc507396eb | ||
|
|
aec2002a9f | ||
|
|
7dd57fb116 | ||
|
|
530e47586e | ||
|
|
e726f953a4 | ||
|
|
5697e313df | ||
|
|
1e57eb5fae | ||
|
|
a423dd9f49 | ||
|
|
6eef592cd8 | ||
|
|
93527fdc02 | ||
|
|
b790676940 | ||
|
|
435cfa46a6 | ||
|
|
e493a9f3d1 | ||
|
|
3bf2654b50 | ||
|
|
f0b604c6dc | ||
|
|
a33a344b40 | ||
|
|
871cccc2ae | ||
|
|
89418ca008 | ||
|
|
f6feb88708 | ||
|
|
5954093999 | ||
|
|
069556d9b6 | ||
|
|
f5372ecd1e | ||
|
|
3599f0bff4 | ||
|
|
efe411933f | ||
|
|
940eaf3a07 | ||
|
|
d399fe6e79 | ||
|
|
582e952187 | ||
|
|
76c2b9d16c | ||
|
|
44a20a5029 | ||
|
|
e0b2a0b6e2 | ||
|
|
7040d908dd | ||
|
|
b3dd57f189 | ||
|
|
7038a36b4d | ||
|
|
c56d6bd627 | ||
|
|
2d916936d6 | ||
|
|
270a03e7c5 | ||
|
|
4174c6226f | ||
|
|
7aec4dd938 | ||
|
|
6faa8b82e8 | ||
|
|
c29193f6c8 | ||
|
|
72741f2188 |
19 changed files with 652 additions and 155 deletions
20
Changelog
20
Changelog
|
|
@ -1,3 +1,23 @@
|
|||
2.6.9: 2019-11-15
|
||||
* feature: Allow creating yearly subscriptions for Generic Products (MR!718)
|
||||
Notes for deployment:
|
||||
- do a db migrate for new column added to Generic Product model
|
||||
./manage.py migrate hosting
|
||||
2.6.8: 2019-11-15
|
||||
* feature: [EU VAT] Add EU VAT feature for generic products (MR!717)
|
||||
Notes for deployment:
|
||||
- do a db migrate a to create VATRates table
|
||||
./manage.py migrate hosting
|
||||
- load vat_rates.csv
|
||||
./manage.py import_vat_rates vat_rates.csv
|
||||
2.6.7: 2019-11-04
|
||||
* bugfix: [admin] Improve dumpuser: show proper dates + bugfix
|
||||
* bugfix: [admin] Improve fetch_stripe_bills:
|
||||
- fix wrong assigment of string to num_invoice_created
|
||||
variable,
|
||||
- return None (do not handle the case) if we don't have an
|
||||
order
|
||||
* bugfix: [admin] Improve deleteuser: do not delete order, bill and vm_detail
|
||||
2.6.6: 2019-11-04
|
||||
* feature: [admin] Add dumpuser management command that dumps a user's data in json (MR!716)
|
||||
2.6.5: 2019-09-24
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-07-03 11:18+0000\n"
|
||||
"POT-Creation-Date: 2019-11-15 17:33+0000\n"
|
||||
"PO-Revision-Date: 2018-03-30 23:22+0000\n"
|
||||
"Last-Translator: b'Anonymous User <coder.purple+25@gmail.com>'\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
|
@ -20,7 +20,7 @@ msgstr ""
|
|||
"X-Translated-Using: django-rosetta 0.8.1\n"
|
||||
|
||||
msgid "CMS Favicon"
|
||||
msgstr ""
|
||||
msgstr "CMS Favicon"
|
||||
|
||||
#, python-format
|
||||
msgid "Your New VM %(vm_name)s at Data Center Light"
|
||||
|
|
@ -52,7 +52,7 @@ msgid "Login"
|
|||
msgstr "Anmelden"
|
||||
|
||||
msgid "Dashboard"
|
||||
msgstr ""
|
||||
msgstr "Dashboard"
|
||||
|
||||
msgid "Thank you for contacting us."
|
||||
msgstr "Nachricht gesendet."
|
||||
|
|
@ -64,7 +64,7 @@ msgid "Get in touch with us!"
|
|||
msgstr "Sende uns eine Nachricht."
|
||||
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
msgstr "Name"
|
||||
|
||||
msgid "Please enter your name."
|
||||
msgstr "Bitte gib Deinen Namen ein."
|
||||
|
|
@ -108,7 +108,7 @@ msgid "Your account details are as follows"
|
|||
msgstr "Deine Account Details sind unten aufgelistet"
|
||||
|
||||
msgid "Username"
|
||||
msgstr "Username"
|
||||
msgstr "Benusername"
|
||||
|
||||
msgid "Your email address"
|
||||
msgstr "Deine E-Mail-Adresse"
|
||||
|
|
@ -155,7 +155,7 @@ msgid "Please enter a value in range %(min_ram)s - 200."
|
|||
msgstr "Bitte gib einen Wert von %(min_ram)s bis 200 ein."
|
||||
|
||||
msgid "VM hosting"
|
||||
msgstr ""
|
||||
msgstr "VM Hosting"
|
||||
|
||||
msgid "month"
|
||||
msgstr "Monat"
|
||||
|
|
@ -207,14 +207,14 @@ msgstr ""
|
|||
|
||||
msgid "Only wants you to pay for what you actually need."
|
||||
msgstr ""
|
||||
"Möchte, dass du nur bezahlst, was du auch wirklich brauchst: Wähle deine "
|
||||
"Du möchtest nur das bezahlen, was du auch wirklich brauchst: Wähle deine "
|
||||
"Ressourcen individuell aus!
"
|
||||
|
||||
msgid ""
|
||||
"Is creative, using a modern and alternative design for a data center in "
|
||||
"order to make it more sustainable and affordable at the same time."
|
||||
msgstr ""
|
||||
"Ist kreativ, indem es sich ein modernes und alternatives Layout zu Nutze "
|
||||
"Es ist kreativ, da es sich ein modernes und alternatives Layout zu Nutze"
|
||||
"macht um Nachhaltigkeit zu fördern und somit erschwingliche Preise bieten zu "
|
||||
"können.
"
|
||||
|
||||
|
|
@ -222,9 +222,9 @@ msgid ""
|
|||
"Cuts down the costs for you by using FOSS (Free Open Source Software) "
|
||||
"exclusively, wherefore we can save money from paying licenses."
|
||||
msgstr ""
|
||||
"Sorgt dafür, dass unnötige Kosten erspart werden, indem es ausschliesslich "
|
||||
"mit FOSS (Free Open Source Software) arbeitet und wir daher auf "
|
||||
"Lizenzgebühren verzichten können.
"
|
||||
"Um unnötige Kosten zu sparen werden, wird ausschliesslich Software auf"
|
||||
"Basis von FOSS (Free Open Source Software) eingesetzt und dadurch können auf "
|
||||
"Lizenzgebühren verzichtet werden.
"
|
||||
|
||||
msgid "Scale out"
|
||||
msgstr "Skalierung"
|
||||
|
|
@ -311,7 +311,7 @@ msgid "Billing Address"
|
|||
msgstr "Rechnungsadresse"
|
||||
|
||||
msgid "Make a payment"
|
||||
msgstr ""
|
||||
msgstr "Tätige eine Bezahlung"
|
||||
|
||||
msgid "Your Order"
|
||||
msgstr "Deine Bestellung"
|
||||
|
|
@ -375,6 +375,9 @@ msgstr "Letzten"
|
|||
msgid "Type"
|
||||
msgstr "Typ"
|
||||
|
||||
msgid "Expiry"
|
||||
msgstr "Ablaufdatum"
|
||||
|
||||
msgid "SELECT"
|
||||
msgstr "AUSWÄHLEN"
|
||||
|
||||
|
|
@ -415,14 +418,23 @@ msgstr "Bestellungsübersicht"
|
|||
msgid "Product"
|
||||
msgstr "Produkt"
|
||||
|
||||
msgid "Price"
|
||||
msgstr "Preise"
|
||||
|
||||
msgid "VAT for"
|
||||
msgstr "MwSt für"
|
||||
|
||||
msgid "Total Amount"
|
||||
msgstr "Gesamtsumme"
|
||||
|
||||
msgid "Amount"
|
||||
msgstr ""
|
||||
msgstr "Betrag"
|
||||
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
msgstr "Beschreibung"
|
||||
|
||||
msgid "Recurring"
|
||||
msgstr ""
|
||||
msgstr "Wiederholend"
|
||||
|
||||
msgid "Subtotal"
|
||||
msgstr "Zwischensumme"
|
||||
|
|
@ -434,12 +446,20 @@ msgstr "Mehrwertsteuer"
|
|||
#| msgid ""
|
||||
#| "By clicking \"Place order\" this plan will charge your credit card "
|
||||
#| "account with %(total_price)s CHF/month"
|
||||
msgid ""
|
||||
"By clicking \"Place order\" this plan will charge your credit card account "
|
||||
"with %(total_price)s CHF/year"
|
||||
msgstr ""
|
||||
"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(total_price)s "
|
||||
"CHF pro Jahr belastet"
|
||||
|
||||
|
||||
msgid ""
|
||||
"By clicking \"Place order\" this plan will charge your credit card account "
|
||||
"with %(total_price)s CHF/month"
|
||||
msgstr ""
|
||||
"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit "
|
||||
"%(vm_total_price)s CHF pro Monat belastet"
|
||||
"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(total_price)s "
|
||||
"CHF pro Monat belastet"
|
||||
|
||||
#, fuzzy, python-format
|
||||
#| msgid ""
|
||||
|
|
@ -470,10 +490,10 @@ msgid "Hold tight, we are processing your request"
|
|||
msgstr "Bitte warten - wir verarbeiten Deine Anfrage gerade"
|
||||
|
||||
msgid "OK"
|
||||
msgstr ""
|
||||
msgstr "Ok"
|
||||
|
||||
msgid "Close"
|
||||
msgstr ""
|
||||
msgstr "Schliessen"
|
||||
|
||||
msgid "Some problem encountered. Please try again later."
|
||||
msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal."
|
||||
|
|
@ -485,7 +505,7 @@ msgid "Tech Stack"
|
|||
msgstr "Tech Stack"
|
||||
|
||||
msgid "We are seriously open source."
|
||||
msgstr "Wir sind vollends opensource."
|
||||
msgstr "Wir sind vollends Open Source."
|
||||
|
||||
msgid ""
|
||||
" Our full software stack is open source – We don't use anything that isn't "
|
||||
|
|
@ -555,13 +575,13 @@ msgid "Starting from only 15CHF per month. Try now."
|
|||
msgstr "Unser Angebot beginnt bei 15 CHF pro Monat. Probier's jetzt aus!"
|
||||
|
||||
msgid "Actions speak louder than words. Let's do it, try our VM now."
|
||||
msgstr "Tagen sagen mehr als Worte – Teste jetzt unsere VM!"
|
||||
msgstr "Taten sagen mehr als Worte – Teste jetzt unsere VM!"
|
||||
|
||||
msgid "Invalid number of cores"
|
||||
msgstr "Ungültige Anzahle CPU-Kerne"
|
||||
|
||||
msgid "Invalid calculator properties"
|
||||
msgstr ""
|
||||
msgstr "Ungültige Berechnungseigenschaften"
|
||||
|
||||
msgid "Invalid RAM size"
|
||||
msgstr "Ungültige RAM-Grösse"
|
||||
|
|
@ -571,7 +591,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}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{user} does not have permission to access the card"
|
||||
|
|
@ -598,11 +618,14 @@ msgid "An error occurred while associating the card. Details: {details}"
|
|||
msgstr ""
|
||||
"Beim Verbinden der Karte ist ein Fehler aufgetreten. Details: {details}"
|
||||
|
||||
msgid "Confirmation of your payment"
|
||||
msgstr ""
|
||||
|
||||
msgid " This is a monthly recurring plan."
|
||||
msgstr ""
|
||||
msgstr "Dies ist ein monatlich wiederkehrender Plan."
|
||||
|
||||
msgid " This is an yearly recurring plan."
|
||||
msgstr "Dies ist ein jährlich wiederkehrender Plan."
|
||||
|
||||
msgid "Confirmation of your payment"
|
||||
msgstr "Bestätigung deiner Zahlung"
|
||||
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
|
|
@ -613,7 +636,8 @@ msgid ""
|
|||
"\n"
|
||||
"Cheers,\n"
|
||||
"Your Data Center Light team"
|
||||
msgstr ""
|
||||
msgstr "Hallo {name},\n" "\n" "vielen Dank für deine Bestellung!\n" "Wir haben deine Bezahlung in Höhe von {amount:.2f} CHF erhalten. {recurring}\n" "\n" "Grüsse\n"
|
||||
"Dein Data Center Light Team"
|
||||
|
||||
msgid "Thank you for the payment."
|
||||
msgstr "Danke für Deine Bestellung."
|
||||
|
|
@ -621,7 +645,7 @@ msgstr "Danke für Deine Bestellung."
|
|||
msgid ""
|
||||
"You will soon receive a confirmation email of the payment. You can always "
|
||||
"contact us at info@ungleich.ch for any question that you may have."
|
||||
msgstr ""
|
||||
msgstr "Du wirst bald eine Bestätigungs-E-Mail über die Zahlung erhalten. Du kannst jederzeit unter info@ungleich.ch kontaktieren."
|
||||
|
||||
msgid "Thank you for the order."
|
||||
msgstr "Danke für Deine Bestellung."
|
||||
|
|
@ -644,9 +668,6 @@ msgstr ""
|
|||
#~ msgid "Card Number"
|
||||
#~ msgstr "Kreditkartennummer"
|
||||
|
||||
#~ msgid "Expiry Date"
|
||||
#~ msgstr "Ablaufdatum"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "You are not making any payment yet. After placing your order, you will be "
|
||||
#~ "taken to the Submit Payment Page."
|
||||
|
|
@ -655,9 +676,6 @@ msgstr ""
|
|||
#~ "ausgelöst, nachdem Du die Bestellung auf der nächsten Seite bestätigt "
|
||||
#~ "hast."
|
||||
|
||||
#~ msgid "Pricing"
|
||||
#~ msgstr "Preise"
|
||||
|
||||
#~ msgid "Order VM"
|
||||
#~ msgstr "VM bestellen"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
import logging
|
||||
import oca
|
||||
import sys
|
||||
import stripe
|
||||
import uuid
|
||||
|
||||
import oca
|
||||
import stripe
|
||||
from django.core.management.base import BaseCommand
|
||||
from membership.models import CustomUser, DeletedUser
|
||||
|
||||
from hosting.models import (
|
||||
HostingOrder, HostingBill, VMDetail, UserCardDetail, UserHostingKey
|
||||
UserCardDetail, UserHostingKey
|
||||
)
|
||||
from membership.models import CustomUser, DeletedUser
|
||||
from opennebula_api.models import OpenNebulaManager
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
|
@ -79,75 +82,6 @@ class Command(BaseCommand):
|
|||
else:
|
||||
logger.error("Error while deleting the StripeCustomer")
|
||||
|
||||
hosting_orders = HostingOrder.objects.filter(
|
||||
customer=stripe_customer.id
|
||||
)
|
||||
|
||||
vm_ids = []
|
||||
for order in hosting_orders:
|
||||
vm_ids.append(order.vm_id)
|
||||
|
||||
# Delete Billing Address
|
||||
if order.billing_address is not None:
|
||||
logger.debug(
|
||||
"Billing Address {} associated with {} deleted"
|
||||
"".format(order.billing_address.id, email)
|
||||
)
|
||||
order.billing_address.delete()
|
||||
else:
|
||||
logger.error(
|
||||
"Error while deleting the billing_address")
|
||||
|
||||
# Delete order
|
||||
if order is not None:
|
||||
logger.debug(
|
||||
"Order {} associated with {} deleted"
|
||||
"".format(order.id, email)
|
||||
)
|
||||
order.delete()
|
||||
else:
|
||||
logger.error(
|
||||
"Error while deleting the Order")
|
||||
|
||||
hosting_bills = HostingBill.objects.filter(
|
||||
customer=stripe_customer.id
|
||||
)
|
||||
|
||||
# delete hosting bills
|
||||
for bill in hosting_bills:
|
||||
if bill.billing_address is not None:
|
||||
logger.debug(
|
||||
"HostingBills billing address {} associated with {} deleted"
|
||||
"".format(bill.billing_address.id, email)
|
||||
)
|
||||
bill.billing_address.delete()
|
||||
else:
|
||||
logger.error(
|
||||
"Error while deleting the HostingBill's Billing address")
|
||||
|
||||
if bill is not None:
|
||||
logger.debug(
|
||||
"HostingBill {} associated with {} deleted"
|
||||
"".format(bill.id, email)
|
||||
)
|
||||
bill.delete()
|
||||
else:
|
||||
logger.error(
|
||||
"Error while deleting the HostingBill")
|
||||
|
||||
# delete VMDetail
|
||||
for vm_id in vm_ids:
|
||||
vm_detail = VMDetail.objects.get(vm_id=vm_id)
|
||||
if vm_detail is not None:
|
||||
logger.debug(
|
||||
"vm_detail {} associated with {} deleted"
|
||||
"".format(vm_detail.id, email)
|
||||
)
|
||||
vm_detail.delete()
|
||||
else:
|
||||
logger.error(
|
||||
"Error while deleting the vm_detail")
|
||||
|
||||
# delete UserCardDetail
|
||||
ucds = UserCardDetail.objects.filter(
|
||||
stripe_customer=stripe_customer
|
||||
|
|
@ -179,8 +113,10 @@ class Command(BaseCommand):
|
|||
user_id = cus_user.id
|
||||
)
|
||||
|
||||
# delete CustomUser
|
||||
cus_user.delete()
|
||||
# reset CustomUser
|
||||
cus_user.email = str(uuid.uuid4())
|
||||
cus_user.validated = 0
|
||||
cus_user.save()
|
||||
|
||||
# remove user from OpenNebula
|
||||
manager = OpenNebulaManager()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import json
|
||||
import logging
|
||||
import sys
|
||||
from pprint import pprint
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from membership.models import CustomUser
|
||||
|
|
@ -19,7 +19,6 @@ class Command(BaseCommand):
|
|||
def handle(self, *args, **options):
|
||||
try:
|
||||
for email in options['customer_email']:
|
||||
logger.debug("Creating dump for the user {}".format(email))
|
||||
try:
|
||||
cus_user = CustomUser.objects.get(email=email)
|
||||
except CustomUser.DoesNotExist as dne:
|
||||
|
|
@ -38,7 +37,7 @@ class Command(BaseCommand):
|
|||
vm_ids.append(order.vm_id)
|
||||
order_dict["VM_ID"] = order.vm_id
|
||||
order_dict["Order Nr."] = order.id
|
||||
order_dict["Created On"] = str(order.created_on)
|
||||
order_dict["Created On"] = str(order.created_at)
|
||||
order_dict["Price"] = order.price
|
||||
order_dict["Payment card details"] = {
|
||||
"last4": order.last4,
|
||||
|
|
@ -82,7 +81,7 @@ class Command(BaseCommand):
|
|||
"IPv6": vm_detail.ipv6,
|
||||
"OS": vm_detail.configuration,
|
||||
}
|
||||
order_dict["Terminated on"] = vm_detail.terminated_at
|
||||
order_dict["Terminated on"] = str(vm_detail.terminated_at)
|
||||
|
||||
orders_dict[order.vm_id] = order_dict
|
||||
|
||||
|
|
@ -116,10 +115,9 @@ class Command(BaseCommand):
|
|||
"Name": uhk.name,
|
||||
"Created on": str(uhk.created_at)
|
||||
}
|
||||
if uhk.private_key is not None:
|
||||
if uhk.private_key:
|
||||
key["Private key"] = uhk.private_key
|
||||
keys[uhk.name] = key
|
||||
print("User {} dump is follows:")
|
||||
output_dict = {
|
||||
"User details": {
|
||||
"Name": cus_user.name,
|
||||
|
|
@ -131,7 +129,6 @@ class Command(BaseCommand):
|
|||
"Payment cards": cards,
|
||||
"SSH Keys": keys
|
||||
}
|
||||
pprint(output_dict)
|
||||
logger.debug("Dumped user {} SUCCESSFULLY.".format(email))
|
||||
print(json.dumps(output_dict, indent=4))
|
||||
except Exception as e:
|
||||
print(" *** Error occurred. Details {}".format(str(e)))
|
||||
|
|
|
|||
|
|
@ -55,10 +55,25 @@
|
|||
</p>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
{% if generic_payment_details.vat_rate > 0 %}
|
||||
<p>
|
||||
<span>{% trans "Price" %}: </span>
|
||||
<strong class="pull-right">CHF {{generic_payment_details.amount_before_vat|floatformat:2|intcomma}}</strong>
|
||||
</p>
|
||||
<p>
|
||||
<span>{% trans "VAT for" %} {{generic_payment_details.vat_country}} ({{generic_payment_details.vat_rate}}%) : </span>
|
||||
<strong class="pull-right">CHF {{generic_payment_details.vat_amount|floatformat:2|intcomma}}</strong>
|
||||
</p>
|
||||
<p>
|
||||
<span>{% trans "Total Amount" %} : </span>
|
||||
<strong class="pull-right">CHF {{generic_payment_details.amount|floatformat:2|intcomma}}</strong>
|
||||
</p>
|
||||
{% else %}
|
||||
<p>
|
||||
<span>{% trans "Amount" %}: </span>
|
||||
<strong class="pull-right">CHF {{generic_payment_details.amount|floatformat:2|intcomma}}</strong>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if generic_payment_details.description %}
|
||||
<p>
|
||||
<span>{% trans "Description" %}: </span>
|
||||
|
|
@ -139,7 +154,11 @@
|
|||
<div class="col-sm-8">
|
||||
{% if generic_payment_details %}
|
||||
{% if generic_payment_details.recurring %}
|
||||
<div class="dcl-place-order-text">{% blocktrans with total_price=generic_payment_details.amount|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with {{total_price}} CHF/month{% endblocktrans %}.</div>
|
||||
{% if generic_payment_details.recurring_interval == 'year' %}
|
||||
<div class="dcl-place-order-text">{% blocktrans with total_price=generic_payment_details.amount|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with {{total_price}} CHF/year{% endblocktrans %}.</div>
|
||||
{% else %}
|
||||
<div class="dcl-place-order-text">{% blocktrans with total_price=generic_payment_details.amount|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with {{total_price}} CHF/month{% endblocktrans %}.</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="dcl-place-order-text">{% blocktrans with total_price=generic_payment_details.amount|floatformat:2|intcomma %}By clicking "Place order" this payment will charge your credit card account with a one time amount of {{total_price}} CHF{% endblocktrans %}.</div>
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ from utils.forms import (
|
|||
BillingAddressForm, BillingAddressFormSignup, UserBillingAddressForm,
|
||||
BillingAddress
|
||||
)
|
||||
from utils.hosting_utils import get_vm_price_with_vat, get_all_public_keys
|
||||
from utils.hosting_utils import (
|
||||
get_vm_price_with_vat, get_all_public_keys, get_vat_rate_for_country
|
||||
)
|
||||
from utils.stripe_utils import StripeUtils
|
||||
from utils.tasks import send_plain_email_task
|
||||
from .cms_models import DCLCalculatorPluginModel
|
||||
|
|
@ -412,10 +414,21 @@ class PaymentOrderView(FormView):
|
|||
product = generic_payment_form.cleaned_data.get(
|
||||
'product_name'
|
||||
)
|
||||
user_country_vat_rate = get_vat_rate_for_country(
|
||||
address_form.cleaned_data["country"]
|
||||
)
|
||||
gp_details = {
|
||||
"product_name": product.product_name,
|
||||
"amount": generic_payment_form.cleaned_data.get(
|
||||
'amount'
|
||||
"vat_rate": user_country_vat_rate * 100,
|
||||
"vat_amount": round(
|
||||
float(product.product_price) *
|
||||
user_country_vat_rate, 2),
|
||||
"vat_country": address_form.cleaned_data["country"],
|
||||
"amount_before_vat": round(
|
||||
float(product.product_price), 2),
|
||||
"amount": product.get_actual_price(
|
||||
vat_rate=get_vat_rate_for_country(
|
||||
address_form.cleaned_data["country"])
|
||||
),
|
||||
"recurring": generic_payment_form.cleaned_data.get(
|
||||
'recurring'
|
||||
|
|
@ -424,7 +437,9 @@ class PaymentOrderView(FormView):
|
|||
'description'
|
||||
),
|
||||
"product_id": product.id,
|
||||
"product_slug": product.product_slug
|
||||
"product_slug": product.product_slug,
|
||||
"recurring_interval":
|
||||
product.product_subscription_interval
|
||||
}
|
||||
request.session["generic_payment_details"] = (
|
||||
gp_details
|
||||
|
|
@ -743,6 +758,7 @@ class OrderConfirmationView(DetailView, FormView):
|
|||
|
||||
if ('generic_payment_type' not in request.session or
|
||||
(request.session['generic_payment_details']['recurring'])):
|
||||
recurring_interval = 'month'
|
||||
if 'generic_payment_details' in request.session:
|
||||
amount_to_be_charged = (
|
||||
round(
|
||||
|
|
@ -755,6 +771,10 @@ class OrderConfirmationView(DetailView, FormView):
|
|||
amount_to_be_charged
|
||||
)
|
||||
stripe_plan_id = plan_name
|
||||
recurring_interval = request.session['generic_payment_details']['recurring_interval']
|
||||
if recurring_interval == "year":
|
||||
plan_name = "{}-yearly".format(plan_name)
|
||||
stripe_plan_id = plan_name
|
||||
else:
|
||||
template = request.session.get('template')
|
||||
specs = request.session.get('specs')
|
||||
|
|
@ -781,7 +801,9 @@ class OrderConfirmationView(DetailView, FormView):
|
|||
stripe_plan = stripe_utils.get_or_create_stripe_plan(
|
||||
amount=amount_to_be_charged,
|
||||
name=plan_name,
|
||||
stripe_plan_id=stripe_plan_id)
|
||||
stripe_plan_id=stripe_plan_id,
|
||||
interval=recurring_interval
|
||||
)
|
||||
subscription_result = stripe_utils.subscribe_customer_to_plan(
|
||||
stripe_api_cus_id,
|
||||
[{"plan": stripe_plan.get(
|
||||
|
|
@ -972,6 +994,9 @@ class OrderConfirmationView(DetailView, FormView):
|
|||
'reply_to': [context['email']],
|
||||
}
|
||||
send_plain_email_task.delay(email_data)
|
||||
recurring_text = _(" This is a monthly recurring plan.")
|
||||
if gp_details['recurring_interval'] == "year":
|
||||
recurring_text = _(" This is an yearly recurring plan.")
|
||||
|
||||
email_data = {
|
||||
'subject': _("Confirmation of your payment"),
|
||||
|
|
@ -985,7 +1010,7 @@ class OrderConfirmationView(DetailView, FormView):
|
|||
name=user.get('name'),
|
||||
amount=gp_details['amount'],
|
||||
recurring=(
|
||||
_(' This is a monthly recurring plan.')
|
||||
recurring_text
|
||||
if gp_details['recurring'] else ''
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -109,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(
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-09-15 03:39+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"
|
||||
|
|
@ -73,7 +76,7 @@ msgid "Please input a proper SSH key"
|
|||
msgstr "Bitte verwende einen gültigen SSH-Key"
|
||||
|
||||
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"
|
||||
|
|
@ -239,7 +242,8 @@ 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."
|
||||
msgstr ""
|
||||
"Du kannst Dich auf Deiner VM mit dem user <strong>puffy</strong> einloggen."
|
||||
|
||||
msgid "View Detail"
|
||||
msgstr "Details anzeigen"
|
||||
|
|
@ -400,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"
|
||||
|
|
@ -418,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"
|
||||
|
|
@ -489,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"
|
||||
|
|
@ -845,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 "
|
||||
|
|
@ -864,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"
|
||||
|
|
|
|||
|
|
@ -50,7 +50,12 @@ 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:
|
||||
logger.error("Did not import invoice for %s"
|
||||
"" % str(invoice))
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS("Number of invoices imported = %s" % num_invoice_created)
|
||||
)
|
||||
|
|
|
|||
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)))
|
||||
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),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import decimal
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
|
@ -78,13 +79,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
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -319,7 +324,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),
|
||||
|
|
@ -704,3 +712,13 @@ class UserCardDetail(AssignPermissionsMixin, models.Model):
|
|||
return ucd
|
||||
except UserCardDetail.DoesNotExist:
|
||||
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='')
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -1268,6 +1268,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))
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import subprocess
|
|||
from oca.pool import WrongIdError
|
||||
|
||||
from datacenterlight.models import VMPricing
|
||||
from hosting.models import UserHostingKey, VMDetail
|
||||
from hosting.models import UserHostingKey, VMDetail, VATRates
|
||||
from opennebula_api.serializers import VirtualMachineSerializer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -150,6 +150,20 @@ def ping_ok(host_ipv6):
|
|||
return True
|
||||
|
||||
|
||||
def get_vat_rate_for_country(country):
|
||||
vat_rate = None
|
||||
try:
|
||||
vat_rate = VATRates.objects.get(
|
||||
territory_codes=country, start_date__isnull=False, stop_date=None
|
||||
)
|
||||
logger.debug("VAT rate for %s is %s" % (country, vat_rate.rate))
|
||||
return vat_rate.rate
|
||||
except VATRates.DoesNotExist as dne:
|
||||
logger.debug(str(dne))
|
||||
logger.debug("Did not find VAT rate for %s, returning 0" % country)
|
||||
return 0
|
||||
|
||||
|
||||
class HostingUtils:
|
||||
@staticmethod
|
||||
def clear_items_from_list(from_list, items_list):
|
||||
|
|
|
|||
|
|
@ -226,7 +226,8 @@ class StripeUtils(object):
|
|||
return charge
|
||||
|
||||
@handleStripeError
|
||||
def get_or_create_stripe_plan(self, amount, name, stripe_plan_id):
|
||||
def get_or_create_stripe_plan(self, amount, name, stripe_plan_id,
|
||||
interval=""):
|
||||
"""
|
||||
This function checks if a StripePlan with the given
|
||||
stripe_plan_id already exists. If it exists then the function
|
||||
|
|
@ -238,6 +239,10 @@ class StripeUtils(object):
|
|||
:param stripe_plan_id: The id of the Stripe plan to be
|
||||
created. Use get_stripe_plan_id_string function to
|
||||
obtain the name of the plan to be created
|
||||
:param interval: str representing the interval of the Plan
|
||||
Specifies billing frequency. Either day, week, month or year.
|
||||
Ref: https://stripe.com/docs/api/plans/create#create_plan-interval
|
||||
The default is month
|
||||
:return: The StripePlan object if it exists else creates a
|
||||
Plan object in Stripe and a local StripePlan and
|
||||
returns it. Returns None in case of Stripe error
|
||||
|
|
@ -245,6 +250,7 @@ class StripeUtils(object):
|
|||
_amount = float(amount)
|
||||
amount = int(_amount * 100) # stripe amount unit, in cents
|
||||
stripe_plan_db_obj = None
|
||||
plan_interval = interval if interval is not "" else self.INTERVAL
|
||||
try:
|
||||
stripe_plan_db_obj = StripePlan.objects.get(
|
||||
stripe_plan_id=stripe_plan_id)
|
||||
|
|
@ -252,7 +258,7 @@ class StripeUtils(object):
|
|||
try:
|
||||
self.stripe.Plan.create(
|
||||
amount=amount,
|
||||
interval=self.INTERVAL,
|
||||
interval=plan_interval,
|
||||
name=name,
|
||||
currency=self.CURRENCY,
|
||||
id=stripe_plan_id)
|
||||
|
|
|
|||
318
vat_rates.csv
Normal file
318
vat_rates.csv
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
start_date,stop_date,territory_codes,currency_code,rate,rate_type,description
|
||||
2011-01-04,,AI,XCD,0,standard,Anguilla (British overseas territory) is exempted of VAT.
|
||||
1984-01-01,,AT,EUR,0.2,standard,Austria (member state) standard VAT rate.
|
||||
1976-01-01,1984-01-01,AT,EUR,0.18,standard,
|
||||
1973-01-01,1976-01-01,AT,EUR,0.16,standard,
|
||||
1984-01-01,,"AT-6691
|
||||
DE-87491",EUR,0.19,standard,Jungholz (Austrian town) special VAT rate.
|
||||
1984-01-01,,"AT-6991
|
||||
AT-6992
|
||||
AT-6993
|
||||
DE-87567
|
||||
DE-87568
|
||||
DE-87569",EUR,0.19,standard,Mittelberg (Austrian town) special VAT rate.
|
||||
1996-01-01,,BE,EUR,0.21,standard,Belgium (member state) standard VAT rate.
|
||||
1994-01-01,1996-01-01,BE,EUR,0.205,standard,
|
||||
1992-04-01,1994-01-01,BE,EUR,0.195,standard,
|
||||
1983-01-01,1992-04-01,BE,EUR,0.19,standard,
|
||||
1981-07-01,1983-01-01,BE,EUR,0.17,standard,
|
||||
1978-07-01,1981-07-01,BE,EUR,0.16,standard,
|
||||
1971-07-01,1978-07-01,BE,EUR,0.18,standard,
|
||||
1999-01-01,,BG,BGN,0.2,standard,Bulgaria (member state) standard VAT rate.
|
||||
1996-07-01,1999-01-01,BG,BGN,0.22,standard,
|
||||
1994-04-01,1996-07-01,BG,BGN,0.18,standard,
|
||||
2011-01-04,,BM,BMD,0,standard,Bermuda (British overseas territory) is exempted of VAT.
|
||||
2014-01-13,,"CY
|
||||
GB-BFPO 57
|
||||
GB-BFPO 58
|
||||
GB-BFPO 59
|
||||
UK-BFPO 57
|
||||
UK-BFPO 58
|
||||
UK-BFPO 59",EUR,0.19,standard,"Cyprus (member state) standard VAT rate.
|
||||
Akrotiri and Dhekelia (British overseas territory) is subjected to Cyprus' standard VAT rate."
|
||||
2013-01-14,2014-01-13,CY,EUR,0.18,standard,
|
||||
2012-03-01,2013-01-14,CY,EUR,0.17,standard,
|
||||
2003-01-01,2012-03-01,CY,EUR,0.15,standard,
|
||||
2002-07-01,2003-01-01,CY,EUR,0.13,standard,
|
||||
2000-07-01,2002-07-01,CY,EUR,0.1,standard,
|
||||
1993-10-01,2000-07-01,CY,EUR,0.08,standard,
|
||||
1992-07-01,1993-10-01,CY,EUR,0.05,standard,
|
||||
2013-01-01,,CZ,CZK,0.21,standard,Czech Republic (member state) standard VAT rate.
|
||||
2010-01-01,2013-01-01,CZ,CZK,0.2,standard,
|
||||
2004-05-01,2010-01-01,CZ,CZK,0.19,standard,
|
||||
1995-01-01,2004-05-01,CZ,CZK,0.22,standard,
|
||||
1993-01-01,1995-01-01,CZ,CZK,0.23,standard,
|
||||
2007-01-01,,DE,EUR,0.19,standard,Germany (member state) standard VAT rate.
|
||||
1998-04-01,2007-01-01,DE,EUR,0.16,standard,
|
||||
1993-01-01,1998-04-01,DE,EUR,0.15,standard,
|
||||
1983-07-01,1993-01-01,DE,EUR,0.14,standard,
|
||||
1979-07-01,1983-07-01,DE,EUR,0.13,standard,
|
||||
1978-01-01,1979-07-01,DE,EUR,0.12,standard,
|
||||
1968-07-01,1978-01-01,DE,EUR,0.11,standard,
|
||||
1968-01-01,1968-07-01,DE,EUR,0.1,standard,
|
||||
2007-01-01,,DE-27498,EUR,0,standard,Heligoland (German island) is exempted of VAT.
|
||||
2007-01-01,,"DE-78266
|
||||
CH-8238",EUR,0,standard,Busingen am Hochrhein (German territory) is exempted of VAT.
|
||||
1992-01-01,,DK,DKK,0.25,standard,Denmark (member state) standard VAT rate.
|
||||
1980-06-30,1992-01-01,DK,DKK,0.22,standard,
|
||||
1978-10-30,1980-06-30,DK,DKK,0.2025,standard,
|
||||
1977-10-03,1978-10-30,DK,DKK,0.18,standard,
|
||||
1970-06-29,1977-10-03,DK,DKK,0.15,standard,
|
||||
1968-04-01,1970-06-29,DK,DKK,0.125,standard,
|
||||
1967-07-03,1968-04-01,DK,DKK,0.1,standard,
|
||||
2009-07-01,,EE,EUR,0.2,standard,Estonia (member state) standard VAT rate.
|
||||
1993-01-01,2009-07-01,EE,EUR,0.18,standard,
|
||||
1991-01-01,1993-01-01,EE,EUR,0.1,standard,
|
||||
2016-06-01,,"GR
|
||||
EL",EUR,0.24,standard,Greece (member state) standard VAT rate.
|
||||
2010-07-01,2016-06-01,"GR
|
||||
EL",EUR,0.23,standard,
|
||||
2010-03-15,2010-07-01,"GR
|
||||
EL",EUR,0.21,standard,
|
||||
2005-04-01,2010-03-15,"GR
|
||||
EL",EUR,0.19,standard,
|
||||
1990-04-28,2005-04-01,"GR
|
||||
EL",EUR,0.18,standard,
|
||||
1988-01-01,1990-04-28,"GR
|
||||
EL",EUR,0.16,standard,
|
||||
1987-01-01,1988-01-01,"GR
|
||||
EL",EUR,0.18,standard,
|
||||
2012-09-01,,ES,EUR,0.21,standard,Spain (member state) standard VAT rate.
|
||||
2010-07-01,2012-09-01,ES,EUR,0.18,standard,
|
||||
1995-01-01,2010-07-01,ES,EUR,0.16,standard,
|
||||
1992-08-01,1995-01-01,ES,EUR,0.15,standard,
|
||||
1992-01-01,1992-08-01,ES,EUR,0.13,standard,
|
||||
1986-01-01,1992-01-01,ES,EUR,0.12,standard,
|
||||
2012-09-01,,"ES-CN
|
||||
ES-GC
|
||||
ES-TF
|
||||
IC",EUR,0,standard,Canary Islands (Spanish autonomous community) is exempted of VAT.
|
||||
2012-09-01,,"ES-ML
|
||||
ES-CE
|
||||
EA",EUR,0,standard,Ceuta and Melilla (Spanish autonomous cities) is exempted of VAT.
|
||||
2013-01-01,,FI,EUR,0.24,standard,Finland (member state) standard VAT rate.
|
||||
2010-07-01,2013-01-01,FI,EUR,0.23,standard,
|
||||
1994-06-01,2010-07-01,FI,EUR,0.22,standard,
|
||||
2013-01-01,,"FI-01
|
||||
AX",EUR,0,standard,Aland Islands (Finish autonomous region) is exempted of VAT.
|
||||
2011-01-04,,FK,FKP,0,standard,Falkland Islands (British overseas territory) is exempted of VAT.
|
||||
1992-01-01,,FO,DKK,0,standard,Faroe Islands (Danish autonomous country) is exempted of VAT.
|
||||
2014-01-01,,"FR
|
||||
MC",EUR,0.2,standard,"France (member state) standard VAT rate.
|
||||
Monaco (sovereign city-state) is member of the EU VAT area and subjected to France's standard VAT rate."
|
||||
2000-04-01,2014-01-01,"FR
|
||||
MC",EUR,0.196,standard,
|
||||
1995-08-01,2000-04-01,"FR
|
||||
MC",EUR,0.206,standard,
|
||||
1982-07-01,1995-08-01,"FR
|
||||
MC",EUR,0.186,standard,
|
||||
1977-01-01,1982-07-01,"FR
|
||||
MC",EUR,0.176,standard,
|
||||
1973-01-01,1977-01-01,"FR
|
||||
MC",EUR,0.2,standard,
|
||||
1970-01-01,1973-01-01,"FR
|
||||
MC",EUR,0.23,standard,
|
||||
1968-12-01,1970-01-01,"FR
|
||||
MC",EUR,0.19,standard,
|
||||
1968-01-01,1968-12-01,"FR
|
||||
MC",EUR,0.1666,standard,
|
||||
2014-01-01,,"FR-BL
|
||||
BL",EUR,0,standard,Saint Barthelemy (French overseas collectivity) is exempted of VAT.
|
||||
2014-01-01,,"FR-GF
|
||||
GF",EUR,0,standard,Guiana (French overseas department) is exempted of VAT.
|
||||
2014-01-01,,"FR-GP
|
||||
GP",EUR,0.085,standard,Guadeloupe (French overseas department) special VAT rate.
|
||||
2014-01-01,,"FR-MF
|
||||
MF",EUR,0,standard,Saint Martin (French overseas collectivity) is subjected to France's standard VAT rate.
|
||||
2014-01-01,,"FR-MQ
|
||||
MQ",EUR,0.085,standard,Martinique (French overseas department) special VAT rate.
|
||||
2014-01-01,,"FR-NC
|
||||
NC",XPF,0,standard,New Caledonia (French special collectivity) is exempted of VAT.
|
||||
2014-01-01,,"FR-PF
|
||||
PF",XPF,0,standard,French Polynesia (French overseas collectivity) is exempted of VAT.
|
||||
2014-01-01,,"FR-PM
|
||||
PM",EUR,0,standard,Saint Pierre and Miquelon (French overseas collectivity) is exempted of VAT.
|
||||
2014-01-01,,"FR-RE
|
||||
RE",EUR,0.085,standard,Reunion (French overseas department) special VAT rate.
|
||||
2014-01-01,,"FR-TF
|
||||
TF",EUR,0,standard,French Southern and Antarctic Lands (French overseas territory) is exempted of VAT.
|
||||
2014-01-01,,"FR-WF
|
||||
WF",XPF,0,standard,Wallis and Futuna (French overseas collectivity) is exempted of VAT.
|
||||
2014-01-01,,"FR-YT
|
||||
YT",EUR,0,standard,Mayotte (French overseas department) is exempted of VAT.
|
||||
2011-01-04,,GG,GBP,0,standard,Guernsey (British Crown dependency) is exempted of VAT.
|
||||
2011-01-04,,GI,GIP,0,standard,Gibraltar (British overseas territory) is exempted of VAT.
|
||||
1992-01-01,,GL,DKK,0,standard,Greenland (Danish autonomous country) is exempted of VAT.
|
||||
2010-07-01,2016-06-01,"GR-34007
|
||||
EL-34007",EUR,0.16,standard,Skyros (Greek island) special VAT rate.
|
||||
2010-07-01,2016-06-01,"GR-37002
|
||||
GR-37003
|
||||
GR-37005
|
||||
EL-37002
|
||||
EL-37003
|
||||
EL-37005",EUR,0.16,standard,Northern Sporades (Greek islands) special VAT rate.
|
||||
2010-07-01,2016-06-01,"GR-64004
|
||||
EL-64004",EUR,0.16,standard,Thasos (Greek island) special VAT rate.
|
||||
2010-07-01,2016-06-01,"GR-68002
|
||||
EL-68002",EUR,0.16,standard,Samothrace (Greek island) special VAT rate.
|
||||
2010-07-01,,"GR-69
|
||||
EL-69",EUR,0,standard,Mount Athos (Greek self-governed part) is exempted of VAT.
|
||||
2010-07-01,2016-06-01,"GR-81
|
||||
EL-81",EUR,0.16,standard,Dodecanese (Greek department) special VAT rate.
|
||||
2010-07-01,2016-06-01,"GR-82
|
||||
EL-82",EUR,0.16,standard,Cyclades (Greek department) special VAT rate.
|
||||
2010-07-01,2016-06-01,"GR-83
|
||||
EL-83",EUR,0.16,standard,Lesbos (Greek department) special VAT rate.
|
||||
2010-07-01,2016-06-01,"GR-84
|
||||
EL-84",EUR,0.16,standard,Samos (Greek department) special VAT rate.
|
||||
2010-07-01,2016-06-01,"GR-85
|
||||
EL-85",EUR,0.16,standard,Chios (Greek department) special VAT rate.
|
||||
2011-01-04,,GS,GBP,0,standard,South Georgia and the South Sandwich Islands (British overseas territory) is exempted of VAT.
|
||||
2012-03-01,,HR,HRK,0.25,standard,Croatia (member state) standard VAT rate.
|
||||
2009-08-01,2012-03-01,HR,HRK,0.23,standard,
|
||||
1998-08-01,2009-08-01,HR,HRK,0.22,standard,
|
||||
2012-01-01,,HU,HUF,0.27,standard,Hungary (member state) standard VAT rate.
|
||||
2009-07-01,2012-01-01,HU,HUF,0.25,standard,
|
||||
2006-01-01,2009-07-01,HU,HUF,0.2,standard,
|
||||
1988-01-01,2006-01-01,HU,HUF,0.25,standard,
|
||||
2012-01-01,,IE,EUR,0.23,standard,Republic of Ireland (member state) standard VAT rate.
|
||||
2010-01-01,2012-01-01,IE,EUR,0.21,standard,
|
||||
2008-12-01,2010-01-01,IE,EUR,0.215,standard,
|
||||
2002-03-01,2008-12-01,IE,EUR,0.21,standard,
|
||||
2001-01-01,2002-03-01,IE,EUR,0.2,standard,
|
||||
1991-03-01,2001-01-01,IE,EUR,0.21,standard,
|
||||
1990-03-01,1991-03-01,IE,EUR,0.23,standard,
|
||||
1986-03-01,1990-03-01,IE,EUR,0.25,standard,
|
||||
1983-05-01,1986-03-01,IE,EUR,0.23,standard,
|
||||
1983-03-01,1983-05-01,IE,EUR,0.35,standard,
|
||||
1982-05-01,1983-03-01,IE,EUR,0.3,standard,
|
||||
1980-05-01,1982-05-01,IE,EUR,0.25,standard,
|
||||
1976-03-01,1980-05-01,IE,EUR,0.2,standard,
|
||||
1973-09-03,1976-03-01,IE,EUR,0.195,standard,
|
||||
1972-11-01,1973-09-03,IE,EUR,0.1637,standard,
|
||||
2011-01-04,,IO,GBP,0,standard,British Indian Ocean Territory (British overseas territory) is exempted of VAT.
|
||||
2013-10-01,,IT,EUR,0.22,standard,Italy (member state) standard VAT rate.
|
||||
2011-09-17,2013-10-01,IT,EUR,0.21,standard,
|
||||
1997-10-01,2011-09-17,IT,EUR,0.2,standard,
|
||||
1988-08-01,1997-10-01,IT,EUR,0.19,standard,
|
||||
1982-08-05,1988-08-01,IT,EUR,0.18,standard,
|
||||
1981-01-01,1982-08-05,IT,EUR,0.15,standard,
|
||||
1980-11-01,1981-01-01,IT,EUR,0.14,standard,
|
||||
1980-07-03,1980-11-01,IT,EUR,0.15,standard,
|
||||
1977-02-08,1980-07-03,IT,EUR,0.14,standard,
|
||||
1973-01-01,1977-02-08,IT,EUR,0.12,standard,
|
||||
2013-10-01,,"IT-22060
|
||||
CH-6911",CHF,0,standard,Campione (Italian town) is exempted of VAT.
|
||||
2013-10-01,,IT-23030,EUR,0,standard,Livigno (Italian town) is exempted of VAT.
|
||||
2011-01-04,,JE,GBP,0,standard,Jersey (British Crown dependency) is exempted of VAT.
|
||||
2011-01-04,,KY,KYD,0,standard,Cayman Islands (British overseas territory) is exempted of VAT.
|
||||
2009-09-01,,LT,EUR,0.21,standard,Lithuania (member state) standard VAT rate.
|
||||
2009-01-01,2009-09-01,LT,EUR,0.19,standard,
|
||||
1994-05-01,2009-01-01,LT,EUR,0.18,standard,
|
||||
2015-01-01,,LU,EUR,0.17,standard,Luxembourg (member state) standard VAT rate.
|
||||
1992-01-01,2015-01-01,LU,EUR,0.15,standard,
|
||||
1983-07-01,1992-01-01,LU,EUR,0.12,standard,
|
||||
1971-01-01,1983-07-01,LU,EUR,0.1,standard,
|
||||
1970-01-01,1971-01-01,LU,EUR,0.8,standard,
|
||||
2012-07-01,,LV,EUR,0.21,standard,Latvia (member state) standard VAT rate.
|
||||
2011-01-01,2012-07-01,LV,EUR,0.22,standard,
|
||||
2009-01-01,2011-01-01,LV,EUR,0.21,standard,
|
||||
1995-05-01,2009-01-01,LV,EUR,0.18,standard,
|
||||
2011-01-04,,MS,XCD,0,standard,Montserrat (British overseas territory) is exempted of VAT.
|
||||
2004-01-01,,MT,EUR,0.18,standard,Malta (member state) standard VAT rate.
|
||||
1995-01-01,2004-01-01,MT,EUR,0.15,standard,
|
||||
2012-10-01,,NL,EUR,0.21,standard,Netherlands (member state) standard VAT rate.
|
||||
2001-01-01,2012-10-01,NL,EUR,0.19,standard,
|
||||
1992-10-01,2001-01-01,NL,EUR,0.175,standard,
|
||||
1989-01-01,1992-10-01,NL,EUR,0.185,standard,
|
||||
1986-10-01,1989-01-01,NL,EUR,0.2,standard,
|
||||
1984-01-01,1986-10-01,NL,EUR,0.19,standard,
|
||||
1976-01-01,1984-01-01,NL,EUR,0.18,standard,
|
||||
1973-01-01,1976-01-01,NL,EUR,0.16,standard,
|
||||
1971-01-01,1973-01-01,NL,EUR,0.14,standard,
|
||||
1969-01-01,1971-01-01,NL,EUR,0.12,standard,
|
||||
2012-10-01,,"NL-AW
|
||||
AW",AWG,0,standard,Aruba (Dutch country) are exempted of VAT.
|
||||
2012-10-01,,"NL-CW
|
||||
NL-SX
|
||||
CW
|
||||
SX",ANG,0,standard,Curacao and Sint Maarten (Dutch countries) are exempted of VAT.
|
||||
2012-10-01,,"NL-BQ1
|
||||
NL-BQ2
|
||||
NL-BQ3
|
||||
BQ
|
||||
BQ-BO
|
||||
BQ-SA
|
||||
BQ-SE",USD,0,standard,"Bonaire, Saba and Sint Eustatius (Dutch special municipalities) are exempted of VAT."
|
||||
2011-01-01,,PL,PLN,0.23,standard,Poland (member state) standard VAT rate.
|
||||
1993-01-08,2011-01-01,PL,PLN,0.22,standard,
|
||||
2011-01-04,,PN,NZD,0,standard,Pitcairn Islands (British overseas territory) is exempted of VAT.
|
||||
2011-01-01,,PT,EUR,0.23,standard,Portugal (member state) standard VAT rate.
|
||||
2010-07-01,2011-01-01,PT,EUR,0.21,standard,
|
||||
2008-07-01,2010-07-01,PT,EUR,0.2,standard,
|
||||
2005-07-01,2008-07-01,PT,EUR,0.21,standard,
|
||||
2002-06-05,2005-07-01,PT,EUR,0.19,standard,
|
||||
1995-01-01,2002-06-05,PT,EUR,0.17,standard,
|
||||
1992-03-24,1995-01-01,PT,EUR,0.16,standard,
|
||||
1988-02-01,1992-03-24,PT,EUR,0.17,standard,
|
||||
1986-01-01,1988-02-01,PT,EUR,0.16,standard,
|
||||
2011-01-01,,PT-20,EUR,0.18,standard,Azores (Portuguese autonomous region) special VAT rate.
|
||||
2011-01-01,,PT-30,EUR,0.22,standard,Madeira (Portuguese autonomous region) special VAT rate.
|
||||
2017-01-01,,RO,RON,0.19,standard,Romania (member state) standard VAT rate.
|
||||
2016-01-01,2017-01-01,RO,RON,0.2,standard,Romania (member state) standard VAT rate.
|
||||
2010-07-01,2016-01-01,RO,RON,0.24,standard,
|
||||
2000-01-01,2010-07-01,RO,RON,0.19,standard,
|
||||
1998-02-01,2000-01-01,RO,RON,0.22,standard,
|
||||
1993-07-01,1998-02-01,RO,RON,0.18,standard,
|
||||
1990-07-01,,SE,SEK,0.25,standard,Sweden (member state) standard VAT rate.
|
||||
1983-01-01,1990-07-01,SE,SEK,0.2346,standard,
|
||||
1981-11-16,1983-01-01,SE,SEK,0.2151,standard,
|
||||
1980-09-08,1981-11-16,SE,SEK,0.2346,standard,
|
||||
1977-06-01,1980-09-08,SE,SEK,0.2063,standard,
|
||||
1971-01-01,1977-06-01,SE,SEK,0.1765,standard,
|
||||
1969-01-01,1971-01-01,SE,SEK,0.1111,standard,
|
||||
2011-01-04,,"AC
|
||||
SH
|
||||
SH-AC
|
||||
SH-HL",SHP,0,standard,Ascension and Saint Helena (British overseas territory) is exempted of VAT.
|
||||
2011-01-04,,"TA
|
||||
SH-TA",GBP,0,standard,Tristan da Cunha (British oversea territory) is exempted of VAT.
|
||||
2013-07-01,,SI,EUR,0.22,standard,Slovenia (member state) standard VAT rate.
|
||||
2002-01-01,2013-07-01,SI,EUR,0.2,standard,
|
||||
1999-07-01,2002-01-01,SI,EUR,0.19,standard,
|
||||
2011-01-01,,SK,EUR,0.2,standard,Slovakia (member state) standard VAT rate.
|
||||
2004-01-01,2011-01-01,SK,EUR,0.19,standard,
|
||||
2003-01-01,2004-01-01,SK,EUR,0.2,standard,
|
||||
1996-01-01,2003-01-01,SK,EUR,0.23,standard,
|
||||
1993-08-01,1996-01-01,SK,EUR,0.25,standard,
|
||||
1993-01-01,1993-08-01,SK,EUR,0.23,standard,
|
||||
2011-01-04,,TC,USD,0,standard,Turks and Caicos Islands (British overseas territory) is exempted of VAT.
|
||||
2011-01-04,,"GB
|
||||
UK
|
||||
IM",GBP,0.2,standard,"United Kingdom (member state) standard VAT rate.
|
||||
Isle of Man (British self-governing dependency) is member of the EU VAT area and subjected to UK's standard VAT rate."
|
||||
2010-01-01,2011-01-04,"GB
|
||||
UK
|
||||
IM",GBP,0.175,standard,
|
||||
2008-12-01,2010-01-01,"GB
|
||||
UK
|
||||
IM",GBP,0.15,standard,
|
||||
1991-04-01,2008-12-01,"GB
|
||||
UK
|
||||
IM",GBP,0.175,standard,
|
||||
1979-06-18,1991-04-01,"GB
|
||||
UK
|
||||
IM",GBP,0.15,standard,
|
||||
1974-07-29,1979-06-18,"GB
|
||||
UK
|
||||
IM",GBP,0.08,standard,
|
||||
1973-04-01,1974-07-29,"GB
|
||||
UK
|
||||
IM",GBP,0.1,standard,
|
||||
2011-01-04,,VG,USD,0,standard,British Virgin Islands (British overseas territory) is exempted of VAT.
|
||||
2014-01-01,,CP,EUR,0,standard,Clipperton Island (French overseas possession) is exempted of VAT.
|
||||
2019-11-15,,CH,CHF,0.077,standard,Switzerland standard VAT (added manually)
|
||||
2019-11-15,,MC,EUR,0.196,standard,Monaco standard VAT (added manually)
|
||||
2019-11-15,,FR,EUR,0.2,standard,France standard VAT (added manually)
|
||||
2019-11-15,,GR,EUR,0.24,standard,Greece standard VAT (added manually)
|
||||
2019-11-15,,GB,EUR,0.2,standard,UK standard VAT (added manually)
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue