diff --git a/matrixhosting/utils.py b/matrixhosting/utils.py
index ed0a7f0..0df0129 100644
--- a/matrixhosting/utils.py
+++ b/matrixhosting/utils.py
@@ -24,6 +24,6 @@ def finalize_order(request, customer, billing_address,
if payment:
#Close the bill as the payment has been added
VMInstance.create_instance(order)
- bill.close()
+ bill.close(status="paid")
return order, bill
\ No newline at end of file
diff --git a/matrixhosting/views.py b/matrixhosting/views.py
index 197cf10..afd50d6 100644
--- a/matrixhosting/views.py
+++ b/matrixhosting/views.py
@@ -30,6 +30,7 @@ import uncloud_pay.stripe as uncloud_stripe
from .models import VMInstance
from .serializers import *
from .utils import *
+from ldap.ldapobject import LDAPObject
logger = logging.getLogger(__name__)
diff --git a/uncloud/.env b/uncloud/.env
index 964945c..f3ecfdd 100644
--- a/uncloud/.env
+++ b/uncloud/.env
@@ -1,6 +1,6 @@
ALLOWED_HOSTS=
STRIPE_KEY=
-STRIPE_PUBLIC_KEY=p
+STRIPE_PUBLIC_KEY=
DATABASE_ENGINE=django.db.backends.sqlite3
DATABASE_NAME=
DATABASE_HOST=
@@ -16,4 +16,14 @@ GITLAB_PROJECT_ID=
GITLAB_OAUTH_TOKEN=
GITLAB_AUTHOR_EMAIL=
GITLAB_AUTHOR_NAME=
-WKHTMLTOPDF_CMD=/usr/local/bin/wkhtmltopdf
\ No newline at end of file
+WKHTMLTOPDF_CMD=
+LDAP_DEFAULT_START_UID=
+AUTH_LDAP_SERVER_HOST=
+AUTH_LDAP_SERVER_URI=
+AUTH_LDAP_BIND_DN=
+AUTH_LDAP_BIND_PASSWORD=
+LDAP_ADMIN_DN=
+LDAP_ADMIN_PASSWORD=
+LDAP_CUSTOMER_GROUP_ID=
+LDAP_CUSTOMER_DN=
+
diff --git a/uncloud/forms.py b/uncloud/forms.py
index 153a49a..135b3c1 100644
--- a/uncloud/forms.py
+++ b/uncloud/forms.py
@@ -1,8 +1,8 @@
from django import forms
from django.contrib.auth.models import User
-
class UserDeleteForm(forms.ModelForm):
class Meta:
model = User
fields = []
+
diff --git a/uncloud/settings.py b/uncloud/settings.py
index 53c8478..0548abf 100644
--- a/uncloud/settings.py
+++ b/uncloud/settings.py
@@ -19,8 +19,19 @@ import environ
from django.core.management.utils import get_random_secret_key
from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion
-
-LOGGING = {}
+LOGGING = {
+ 'version': 1,
+ 'disable_existing_loggers': False,
+ 'handlers': {
+ 'console': {
+ 'class': 'logging.StreamHandler',
+ },
+ },
+ 'root': {
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ },
+}
# Initialise environment variables
env = environ.Env()
@@ -135,25 +146,40 @@ AUTH_PASSWORD_VALIDATORS = [
# Authall Settings
ACCOUNT_AUTHENTICATION_METHOD = "username"
ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS = 1
-ACCOUNT_EMAIL_REQUIRED = False
-ACCOUNT_EMAIL_VERIFICATION = "optional"
-ACCOUNT_UNIQUE_EMAIL = False
+ACCOUNT_EMAIL_REQUIRED = True
+ACCOUNT_UNIQUE_EMAIL = True
+MAX_EMAIL_ADDRESSES = 1
################################################################################
# AUTH/LDAP
-AUTH_LDAP_SERVER_URI = ""
-AUTH_LDAP_BIND_DN = ""
-AUTH_LDAP_BIND_PASSWORD = ""
-AUTH_LDAP_USER_SEARCH = LDAPSearch("dc=example,dc=com",
+LDAP_ENABLED = True
+AUTH_LDAP_SERVER_HOST = env('AUTH_LDAP_SERVER_HOST')
+AUTH_LDAP_SERVER_URI = env('AUTH_LDAP_SERVER_URI')
+AUTH_LDAP_BIND_DN = env('AUTH_LDAP_BIND_DN')
+AUTH_LDAP_BIND_PASSWORD = env('AUTH_LDAP_BIND_PASSWORD')
+
+AUTH_LDAP_USER_DN_TEMPLATE = "uid=%(user)s,ou=customers,dc=ungleich,dc=ch"
+AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=customers,dc=ungleich,dc=ch",
ldap.SCOPE_SUBTREE,
"(uid=%(user)s)")
+# BIND_AS_AUTHENTICATING_USER = True
+START_TLS = True
+LDAP_ADMIN_DN = env("LDAP_ADMIN_DN")
+LDAP_ADMIN_PASSWORD = env("LDAP_ADMIN_PASSWORD")
+LDAP_CUSTOMER_GROUP_ID = env("LDAP_CUSTOMER_GROUP_ID")
+LDAP_CUSTOMER_DN=env("LDAP_CUSTOMER_DN")
+#AUTH_LDAP_USER_QUERY_FIELD = "email"
AUTH_LDAP_USER_ATTR_MAP = {
- "first_name": "givenName",
+ "first_name": "cn",
"last_name": "sn",
"email": "mail"
}
+LDAP_DEFAULT_START_UID = int(env('LDAP_DEFAULT_START_UID'))
+LDAP_MAX_UID_FILE_PATH = os.environ.get('LDAP_MAX_UID_FILE_PATH',
+ os.path.join(os.path.abspath(os.path.dirname(__file__)), 'ldap_max_uid_file')
+)
################################################################################
# AUTH/Django
AUTHENTICATION_BACKENDS = [
@@ -162,9 +188,16 @@ AUTHENTICATION_BACKENDS = [
'allauth.account.auth_backends.AuthenticationBackend',
]
-AUTH_USER_MODEL = 'uncloud_auth.User'
+AUTH_USER_MODEL = 'uncloud_auth.User'
+ACCOUNT_FORMS = {
+ 'signup': 'uncloud_auth.forms.MySignupForm',
+ 'change_password': 'uncloud_auth.forms.MyChangePasswordForm',
+ 'set_password': 'uncloud_auth.forms.MySetPasswordForm',
+ 'reset_password_from_key': 'uncloud_auth.forms.MyResetPasswordKeyForm',
+ }
+
################################################################################
# AUTH/REST
REST_FRAMEWORK = {
@@ -233,26 +266,14 @@ UNCLOUD_ADMIN_NAME = "uncloud-admin"
LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL = '/'
-# replace these in local_settings.py
-AUTH_LDAP_SERVER_URI = "ldaps://ldap1.example.com,ldaps://ldap2.example.com"
-AUTH_LDAP_BIND_DN="uid=django,ou=system,dc=example,dc=com"
-AUTH_LDAP_BIND_PASSWORD="a very secure ldap password"
-AUTH_LDAP_USER_SEARCH = LDAPSearch("dc=example,dc=com",
- ldap.SCOPE_SUBTREE,
- "(uid=%(user)s)")
-
-# where to create customers
-LDAP_CUSTOMER_DN="ou=customer,dc=example,dc=com"
-
EMAIL_USE_TLS = True
EMAIL_HOST = env('EMAIL_HOST')
+
EMAIL_PORT = 25
EMAIL_HOST_USER = DEFAULT_FROM_EMAIL = env('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = env('EMAIL_HOST_PASSWORD')
DEFAULT_FROM_EMAIL = 'support@ungleich.ch'
RENEWAL_FROM_EMAIL = 'support@ungleich.ch'
-# Should be removed in production
-EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
##############
# Jobs
diff --git a/uncloud_auth/forms.py b/uncloud_auth/forms.py
new file mode 100644
index 0000000..11ec9c0
--- /dev/null
+++ b/uncloud_auth/forms.py
@@ -0,0 +1,69 @@
+import logging
+
+from allauth.account.forms import SignupForm, ChangePasswordForm, ResetPasswordKeyForm, SetPasswordForm
+from django import forms as d_forms
+from django.conf import settings
+from django.utils.translation import gettext_lazy as _
+from .ungleich_ldap import LdapManager
+
+logger = logging.getLogger(__name__)
+
+class MySignupForm(SignupForm):
+ first_name = d_forms.CharField(max_length=30)
+ last_name = d_forms.CharField(max_length=30)
+
+ def custom_signup(self, request, user):
+ user.first_name = self.cleaned_data["first_name"]
+ user.last_name = self.cleaned_data["last_name"]
+ user.save()
+ if settings.LDAP_ENABLED:
+ ldap_manager = LdapManager()
+ try:
+ user_exists_in_ldap, entries = ldap_manager.check_user_exists(user.username)
+ except Exception:
+ logger.exception("Exception occur while searching for user in LDAP")
+ else:
+ if not user_exists_in_ldap:
+ ldap_manager.create_user(user.username, user.password, user.first_name, user.last_name, user.email)
+
+class MyResetPasswordKeyForm(ResetPasswordKeyForm):
+ def save(self):
+ ldap_manager = LdapManager()
+ try:
+ user_exists_in_ldap, entries = ldap_manager.check_user_exists(self.user.username)
+ if not user_exists_in_ldap:
+ super(MyResetPasswordKeyForm, self).save()
+ else:
+ if ldap_manager.change_password(entries[0].entry_dn, self.cleaned_data["password1"]):
+ super(MyResetPasswordKeyForm, self).save()
+ except Exception:
+ logger.exception("Exception occur while searching for user in LDAP")
+ raise d_forms.ValidationError(_("An error occurred, please try again later"))
+
+class MyChangePasswordForm(ChangePasswordForm):
+ def save(self):
+ ldap_manager = LdapManager()
+ try:
+ user_exists_in_ldap, entries = ldap_manager.check_user_exists(self.user.username)
+ if not user_exists_in_ldap:
+ super(MyChangePasswordForm, self).save()
+ else:
+ if ldap_manager.change_password(self.user.username, self.cleaned_data["password1"]):
+ super(MyChangePasswordForm, self).save()
+ except Exception:
+ logger.exception("Exception occur while searching for user in LDAP")
+ raise d_forms.ValidationError(_("An error occurred, please try again later"))
+
+class MySetPasswordForm(SetPasswordForm):
+ def save(self):
+ ldap_manager = LdapManager()
+ try:
+ user_exists_in_ldap, entries = ldap_manager.check_user_exists(self.user.username)
+ if not user_exists_in_ldap:
+ super(MySetPasswordForm, self).save()
+ else:
+ if ldap_manager.change_password(self.user.username, self.cleaned_data["password1"]):
+ super(MySetPasswordForm, self).save()
+ except Exception:
+ logger.exception("Exception occur while searching for user in LDAP")
+ raise d_forms.ValidationError(_("An error occurred, please try again later"))
diff --git a/uncloud_auth/uldap.py b/uncloud_auth/uldap.py
deleted file mode 100644
index aa90c77..0000000
--- a/uncloud_auth/uldap.py
+++ /dev/null
@@ -1,42 +0,0 @@
-import ldap
-# from django.conf import settings
-
-AUTH_LDAP_SERVER_URI = "ldaps://ldap1.ungleich.ch,ldaps://ldap2.ungleich.ch"
-AUTH_LDAP_BIND_DN="uid=django-create,ou=system,dc=ungleich,dc=ch"
-AUTH_LDAP_BIND_PASSWORD="kS#e+v\zjKn]L!,RIu2}V+DUS"
-# AUTH_LDAP_USER_SEARCH = LDAPSearch("dc=ungleich,dc=ch",
-# ldap.SCOPE_SUBTREE,
-# "(uid=%(user)s)")
-
-
-
-ldap_object = ldap.initialize(AUTH_LDAP_SERVER_URI)
-cancelid = ldap_object.bind(AUTH_LDAP_BIND_DN, AUTH_LDAP_BIND_PASSWORD)
-
-res = ldap_object.search_s("dc=ungleich,dc=ch", ldap.SCOPE_SUBTREE, "(uid=nico)")
-print(res)
-
-# class LDAP(object):
-# """
-# Managing users in LDAP
-
-# Requires the following settings?
-
-# LDAP_USER_DN: where to create users in the tree
-
-# LDAP_ADMIN_DN: which DN to use for managing users
-# LDAP_ADMIN_PASSWORD: which password to used
-
-# This module will reuse information from djagno_auth_ldap, including:
-
-# AUTH_LDAP_SERVER_URI
-
-# """
-# def __init__(self):
-# pass
-
-# def create_user(self):
-# pass
-
-# def change_password(self):
-# pass
diff --git a/uncloud_auth/ungleich_ldap.py b/uncloud_auth/ungleich_ldap.py
index f22b423..39d8bf5 100644
--- a/uncloud_auth/ungleich_ldap.py
+++ b/uncloud_auth/ungleich_ldap.py
@@ -4,7 +4,9 @@ import logging
import random
import ldap3
+from ldap3 import ALL
from django.conf import settings
+from django.contrib.auth.hashers import make_password
logger = logging.getLogger(__name__)
@@ -21,7 +23,7 @@ class LdapManager:
Initialize the LDAP subsystem.
"""
self.rng = random.SystemRandom()
- self.server = ldap3.Server(settings.AUTH_LDAP_SERVER)
+ self.server = ldap3.Server(settings.AUTH_LDAP_SERVER_HOST, use_ssl=True, get_info=ALL)
def get_admin_conn(self):
@@ -32,6 +34,7 @@ class LdapManager:
conn = self.get_conn(user=settings.LDAP_ADMIN_DN,
password=settings.LDAP_ADMIN_PASSWORD,
raise_exceptions=True)
+ conn.start_tls()
conn.bind()
return conn
@@ -58,7 +61,6 @@ class LdapManager:
base64 or decoding it to unicode from ``ascii``.
"""
SALT_BYTES = 15
-
sha1 = hashlib.sha1()
salt = self.rng.getrandbits(SALT_BYTES * 8).to_bytes(SALT_BYTES,
"little")
@@ -67,10 +69,11 @@ class LdapManager:
digest = sha1.digest()
passwd = b"{SSHA}" + base64.b64encode(digest + salt)
+
return passwd
- def create_user(self, user, password, firstname, lastname, email):
+ def create_user(self, username, password, firstname, lastname, email):
conn = self.get_admin_conn()
uidNumber = self._get_max_uid() + 1
logger.debug("uidNumber={uidNumber}".format(uidNumber=uidNumber))
@@ -91,7 +94,7 @@ class LdapManager:
logger.debug("{uid} does not exist. Using it".format(uid=uidNumber))
self._set_max_uid(uidNumber)
try:
- uid = user # user.encode("utf-8")
+ uid = username
conn.add("uid={uid},{customer_dn}".format(
uid=uid, customer_dn=settings.LDAP_CUSTOMER_DN
),
@@ -105,54 +108,43 @@ class LdapManager:
"uidNumber": [str(uidNumber)],
"gidNumber": [str(settings.LDAP_CUSTOMER_GROUP_ID)],
"loginShell": ["/bin/bash"],
- "homeDirectory": ["/home/{}".format(user).encode("utf-8")],
+ "homeDirectory": ["/home/{}".format(username).encode("utf-8")],
"mail": email.encode("utf-8"),
- "userPassword": [self._ssha_password(
- password.encode("utf-8")
- )]
+ "userPassword": [self._ssha_password(password.encode("utf-8"))]
}
)
- logger.debug('Created user %s %s' % (user.encode('utf-8'),
+ logger.debug('Created user %s %s' % (username.encode('utf-8'),
uidNumber))
except Exception as ex:
- logger.debug('Could not create user %s' % user.encode('utf-8'))
+ logger.debug('Could not create user %s' % username.encode('utf-8'))
logger.error("Exception: " + str(ex))
raise
finally:
conn.unbind()
- def change_password(self, uid, new_password):
+ def change_password(self, entry_dn, new_password):
"""
Changes the password of the user identified by user_dn
- :param uid: str The uid that identifies the user
+ :param entry_dn: str The dn that identifies the user
:param new_password: str The new password string
:return: True if password was changed successfully False otherwise
"""
conn = self.get_admin_conn()
-
- # Make sure the user exists first to change his/her details
- user_exists, entries = self.check_user_exists(
- uid=uid,
- search_base=settings.ENTIRE_SEARCH_BASE
- )
return_val = False
- if user_exists:
- try:
- return_val = conn.modify(
- entries[0].entry_dn,
- {
- "userpassword": (
- ldap3.MODIFY_REPLACE,
- [self._ssha_password(new_password.encode("utf-8"))]
- )
- }
- )
- except Exception as ex:
- logger.error("Exception: " + str(ex))
- else:
- logger.error("User {} not found".format(uid))
+ try:
+ return_val = conn.modify(
+ entry_dn,
+ {
+ "userpassword": (
+ ldap3.MODIFY_REPLACE,
+ [self._ssha_password(new_password.encode("utf-8"))]
+ )
+ }
+ )
+ except Exception as ex:
+ logger.error("Exception: " + str(ex))
conn.unbind()
return return_val
@@ -173,7 +165,7 @@ class LdapManager:
# Make sure the user exists first to change his/her details
user_exists, entries = self.check_user_exists(
uid=uid,
- search_base=settings.ENTIRE_SEARCH_BASE
+ search_base=settings.LDAP_CUSTOMER_DN
)
return_val = False
diff --git a/uncloud_pay/migrations/0031_auto_20210819_1304.py b/uncloud_pay/migrations/0031_auto_20210819_1304.py
new file mode 100644
index 0000000..a3d75db
--- /dev/null
+++ b/uncloud_pay/migrations/0031_auto_20210819_1304.py
@@ -0,0 +1,22 @@
+# Generated by Django 3.2.4 on 2021-08-19 13:04
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('uncloud_pay', '0030_pricingplan_monthly_maintenance_fees'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='bill',
+ name='is_closed',
+ ),
+ migrations.AddField(
+ model_name='bill',
+ name='status',
+ field=models.CharField(choices=[('new', 'New'), ('cancelled', 'Cancelled'), ('paid', 'Paid')], default='new', max_length=32),
+ ),
+ ]
diff --git a/uncloud_pay/models.py b/uncloud_pay/models.py
index 20bed38..4e9949b 100644
--- a/uncloud_pay/models.py
+++ b/uncloud_pay/models.py
@@ -27,6 +27,12 @@ from .services import *
# Used to generate bill due dates.
BILL_PAYMENT_DELAY=datetime.timedelta(days=settings.BILL_PAYMENT_DELAY)
+EU_COUNTRIES = ['at', 'be', 'bg', 'ch', 'cy', 'cz', 'hr', 'dk',
+ 'ee', 'fi', 'fr', 'mc', 'de', 'gr', 'hu', 'ie', 'it',
+ 'lv', 'lu', 'mt', 'nl', 'po', 'pt', 'ro','sk', 'si', 'es',
+ 'se', 'gb']
+
+
# Initialize logger.
logger = logging.getLogger(__name__)
@@ -254,41 +260,26 @@ class VATRate(models.Model):
description = models.TextField(blank=True, default='')
@staticmethod
- def get_for_country(country_code):
- vat_rate = None
- try:
- vat_rate = VATRate.objects.get(
- territory_codes=country_code, start_date__isnull=False, stop_date=None
- )
- return vat_rate.rate
- except VATRate.DoesNotExist as dne:
- logger.debug(str(dne))
- logger.debug("Did not find VAT rate for %s, returning 0" % country_code)
- return 0
-
- @staticmethod
- def get_vat_rate(billing_address, when=None):
+ def get_vat_rate_for_country(country, when=None):
"""
Returns the VAT rate for business to customer.
B2B is always 0% with the exception of trading within the own country
"""
-
- country = billing_address.country
-
- # Need to have a provider country
- providers = UncloudProvider.objects.all()
vatrate = filter_for_when(VATRate.objects.filter(territory_codes=country), when).first()
- if not providers and not vatrate:
- return 0
-
- uncloud_provider = filter_for_when(providers).get()
-
# By default we charge VAT. This affects:
# - Same country sales (VAT applied)
# - B2C to EU (VAT applied)
rate = vatrate.rate if vatrate else 0
+ if not country.lower().strip() in EU_COUNTRIES:
+ rate = 0
+
+ return rate
+
+ @staticmethod
+ def get_vat_rate(billing_address, when=None):
+ rate = VATRate.get_vat_rate_for_country(billing_address.country, when)
# Exception: if...
# - the billing_address is in EU,
@@ -296,7 +287,7 @@ class VATRate(models.Model):
# - the vat_number has been verified
# Then we do not charge VAT
- if uncloud_provider.country != country and billing_address.vat_number and billing_address.vat_number_verified:
+ if rate != 0 and billing_address.vat_number and billing_address.vat_number_verified:
rate = 0
return rate
@@ -408,7 +399,7 @@ class Product(models.Model):
def __str__(self):
- return f"{self.name} - {self.description}"
+ return f"{self.name}"
@property
def recurring_orders(self):
@@ -1010,7 +1001,11 @@ class Bill(models.Model):
# FIXME: editable=True -> is in the admin, but also editable in DRF
# Maybe filter fields in the serializer?
- is_closed = models.BooleanField(default=False)
+ status = models.CharField(max_length=32, choices= (
+ ('new', 'New'),
+ ('cancelled', 'Cancelled'),
+ ('paid', 'Paid')
+ ), null=False, blank=False, default="new")
class Meta:
constraints = [
@@ -1020,11 +1015,11 @@ class Bill(models.Model):
name='one_bill_per_month_per_user')
]
- def close(self):
+ def close(self, status):
"""
Close/finish a bill
"""
- self.is_closed = True
+ self.status = status
if not self.ending_date:
self.ending_date = timezone.now()
self.save()
@@ -1120,7 +1115,7 @@ class Bill(models.Model):
# Get date & bill from previous bill, if it exists
if last_bill:
- if not last_bill.is_closed:
+ if last_bill.status == 'new':
bill = last_bill
starting_date = last_bill.starting_date
ending_date = bill.ending_date
diff --git a/uncloud_pay/serializers.py b/uncloud_pay/serializers.py
index 020f359..9f52574 100644
--- a/uncloud_pay/serializers.py
+++ b/uncloud_pay/serializers.py
@@ -122,7 +122,7 @@ class BillSerializer(serializers.ModelSerializer):
model = Bill
fields = ['owner', 'sum', 'vat_rate',
'due_date', 'creation_date', 'starting_date', 'ending_date',
- 'records', 'is_closed', 'billing_address']
+ 'records', 'status', 'billing_address']
# We do not want users to mutate the country / VAT number of an address, as it
# will change VAT on existing bills.
diff --git a/uncloud_pay/tests.py b/uncloud_pay/tests.py
index 7bfc76b..4f2aded 100644
--- a/uncloud_pay/tests.py
+++ b/uncloud_pay/tests.py
@@ -425,7 +425,7 @@ class BillTestCase(TestCase):
self.assertEqual(record.quantity, 1)
self.assertEqual(record.sum, 35)
#close the bill as it has been paid
- bill.close()
+ bill.close(status="paid")
bill2 = Bill.create_next_bill_for_user_address(self.user_addr)
self.assertNotEqual(bill.id, bill2.id)
self.assertEqual(order.billrecord_set.count(), 2)
@@ -483,7 +483,7 @@ class BillTestCase(TestCase):
for ending_date in self.bill_dates:
b = Bill.create_next_bill_for_user_address(self.recurring_user_addr, ending_date)
- b.close()
+ b.close(status="paid")
bill_count = Bill.objects.filter(owner=self.recurring_user).count()
@@ -519,14 +519,28 @@ class VATRatesTestCase(TestCase):
street="unknown",
city="unknown",
postal_code="unknown",
+ country="CH",
active=True)
UncloudNetwork.populate_db_defaults()
UncloudProvider.populate_db_defaults()
+ VATRate.objects.create(territory_codes="CH", currency_code="CHF", rate=7.7,
+ starting_date=timezone.make_aware(datetime.datetime(2000,1,1)))
+
def test_get_rate_for_user(self):
"""
Raise an error, when there is no address
"""
+ rate = VATRate.get_vat_rate(self.user_addr)
+ self.assertEqual(rate, 7.7)
+ self.user_addr.vat_number_verified = True
+ self.user_addr.vat_number = "11111"
+ rate1 = VATRate.get_vat_rate(self.user_addr)
+ self.assertEqual(rate1, 0)
+ rate2 = VATRate.get_vat_rate_for_country('CH')
+ self.assertEqual(rate, 7.7)
+ rate2 = VATRate.get_vat_rate_for_country('EG')
+ self.assertEqual(rate2, 0)
diff --git a/uncloud_pay/views.py b/uncloud_pay/views.py
index b657042..d221de6 100644
--- a/uncloud_pay/views.py
+++ b/uncloud_pay/views.py
@@ -41,11 +41,16 @@ class PricingView(View):
vat_rate = False
vat_validation_status = False
address = False
+ selected_country = request.GET.get('country', False)
if self.request.user and self.request.user.is_authenticated:
- address = get_billing_address_for_user(self.request.user)
- if address:
+ address = get_billing_address_for_user(self.request.user)
+ if address and (address.country == selected_country or not selected_country):
vat_rate = VATRate.get_vat_rate(address)
vat_validation_status = "verified" if address.vat_number_validated_on and address.vat_number_verified else False
+ elif selected_country:
+ vat_rate = VATRate.get_vat_rate_for_country(selected_country)
+ vat_validation_status = False
+
pricing = get_order_total_with_vat(
request.GET.get('cores'),
request.GET.get('memory'),