phase in configuration - move address to base

This commit is contained in:
Nico Schottelius 2020-10-11 22:32:08 +02:00
parent fe4e200dc0
commit bbc7625550
22 changed files with 668 additions and 300 deletions

View File

@ -0,0 +1,243 @@
from django.utils.translation import gettext_lazy as _
# http://xml.coverpages.org/country3166.html
COUNTRIES = (
('AD', _('Andorra')),
('AE', _('United Arab Emirates')),
('AF', _('Afghanistan')),
('AG', _('Antigua & Barbuda')),
('AI', _('Anguilla')),
('AL', _('Albania')),
('AM', _('Armenia')),
('AN', _('Netherlands Antilles')),
('AO', _('Angola')),
('AQ', _('Antarctica')),
('AR', _('Argentina')),
('AS', _('American Samoa')),
('AT', _('Austria')),
('AU', _('Australia')),
('AW', _('Aruba')),
('AZ', _('Azerbaijan')),
('BA', _('Bosnia and Herzegovina')),
('BB', _('Barbados')),
('BD', _('Bangladesh')),
('BE', _('Belgium')),
('BF', _('Burkina Faso')),
('BG', _('Bulgaria')),
('BH', _('Bahrain')),
('BI', _('Burundi')),
('BJ', _('Benin')),
('BM', _('Bermuda')),
('BN', _('Brunei Darussalam')),
('BO', _('Bolivia')),
('BR', _('Brazil')),
('BS', _('Bahama')),
('BT', _('Bhutan')),
('BV', _('Bouvet Island')),
('BW', _('Botswana')),
('BY', _('Belarus')),
('BZ', _('Belize')),
('CA', _('Canada')),
('CC', _('Cocos (Keeling) Islands')),
('CF', _('Central African Republic')),
('CG', _('Congo')),
('CH', _('Switzerland')),
('CI', _('Ivory Coast')),
('CK', _('Cook Iislands')),
('CL', _('Chile')),
('CM', _('Cameroon')),
('CN', _('China')),
('CO', _('Colombia')),
('CR', _('Costa Rica')),
('CU', _('Cuba')),
('CV', _('Cape Verde')),
('CX', _('Christmas Island')),
('CY', _('Cyprus')),
('CZ', _('Czech Republic')),
('DE', _('Germany')),
('DJ', _('Djibouti')),
('DK', _('Denmark')),
('DM', _('Dominica')),
('DO', _('Dominican Republic')),
('DZ', _('Algeria')),
('EC', _('Ecuador')),
('EE', _('Estonia')),
('EG', _('Egypt')),
('EH', _('Western Sahara')),
('ER', _('Eritrea')),
('ES', _('Spain')),
('ET', _('Ethiopia')),
('FI', _('Finland')),
('FJ', _('Fiji')),
('FK', _('Falkland Islands (Malvinas)')),
('FM', _('Micronesia')),
('FO', _('Faroe Islands')),
('FR', _('France')),
('FX', _('France, Metropolitan')),
('GA', _('Gabon')),
('GB', _('United Kingdom (Great Britain)')),
('GD', _('Grenada')),
('GE', _('Georgia')),
('GF', _('French Guiana')),
('GH', _('Ghana')),
('GI', _('Gibraltar')),
('GL', _('Greenland')),
('GM', _('Gambia')),
('GN', _('Guinea')),
('GP', _('Guadeloupe')),
('GQ', _('Equatorial Guinea')),
('GR', _('Greece')),
('GS', _('South Georgia and the South Sandwich Islands')),
('GT', _('Guatemala')),
('GU', _('Guam')),
('GW', _('Guinea-Bissau')),
('GY', _('Guyana')),
('HK', _('Hong Kong')),
('HM', _('Heard & McDonald Islands')),
('HN', _('Honduras')),
('HR', _('Croatia')),
('HT', _('Haiti')),
('HU', _('Hungary')),
('ID', _('Indonesia')),
('IE', _('Ireland')),
('IL', _('Israel')),
('IN', _('India')),
('IO', _('British Indian Ocean Territory')),
('IQ', _('Iraq')),
('IR', _('Islamic Republic of Iran')),
('IS', _('Iceland')),
('IT', _('Italy')),
('JM', _('Jamaica')),
('JO', _('Jordan')),
('JP', _('Japan')),
('KE', _('Kenya')),
('KG', _('Kyrgyzstan')),
('KH', _('Cambodia')),
('KI', _('Kiribati')),
('KM', _('Comoros')),
('KN', _('St. Kitts and Nevis')),
('KP', _('Korea, Democratic People\'s Republic of')),
('KR', _('Korea, Republic of')),
('KW', _('Kuwait')),
('KY', _('Cayman Islands')),
('KZ', _('Kazakhstan')),
('LA', _('Lao People\'s Democratic Republic')),
('LB', _('Lebanon')),
('LC', _('Saint Lucia')),
('LI', _('Liechtenstein')),
('LK', _('Sri Lanka')),
('LR', _('Liberia')),
('LS', _('Lesotho')),
('LT', _('Lithuania')),
('LU', _('Luxembourg')),
('LV', _('Latvia')),
('LY', _('Libyan Arab Jamahiriya')),
('MA', _('Morocco')),
('MC', _('Monaco')),
('MD', _('Moldova, Republic of')),
('MG', _('Madagascar')),
('MH', _('Marshall Islands')),
('ML', _('Mali')),
('MN', _('Mongolia')),
('MM', _('Myanmar')),
('MO', _('Macau')),
('MP', _('Northern Mariana Islands')),
('MQ', _('Martinique')),
('MR', _('Mauritania')),
('MS', _('Monserrat')),
('MT', _('Malta')),
('MU', _('Mauritius')),
('MV', _('Maldives')),
('MW', _('Malawi')),
('MX', _('Mexico')),
('MY', _('Malaysia')),
('MZ', _('Mozambique')),
('NA', _('Namibia')),
('NC', _('New Caledonia')),
('NE', _('Niger')),
('NF', _('Norfolk Island')),
('NG', _('Nigeria')),
('NI', _('Nicaragua')),
('NL', _('Netherlands')),
('NO', _('Norway')),
('NP', _('Nepal')),
('NR', _('Nauru')),
('NU', _('Niue')),
('NZ', _('New Zealand')),
('OM', _('Oman')),
('PA', _('Panama')),
('PE', _('Peru')),
('PF', _('French Polynesia')),
('PG', _('Papua New Guinea')),
('PH', _('Philippines')),
('PK', _('Pakistan')),
('PL', _('Poland')),
('PM', _('St. Pierre & Miquelon')),
('PN', _('Pitcairn')),
('PR', _('Puerto Rico')),
('PT', _('Portugal')),
('PW', _('Palau')),
('PY', _('Paraguay')),
('QA', _('Qatar')),
('RE', _('Reunion')),
('RO', _('Romania')),
('RU', _('Russian Federation')),
('RW', _('Rwanda')),
('SA', _('Saudi Arabia')),
('SB', _('Solomon Islands')),
('SC', _('Seychelles')),
('SD', _('Sudan')),
('SE', _('Sweden')),
('SG', _('Singapore')),
('SH', _('St. Helena')),
('SI', _('Slovenia')),
('SJ', _('Svalbard & Jan Mayen Islands')),
('SK', _('Slovakia')),
('SL', _('Sierra Leone')),
('SM', _('San Marino')),
('SN', _('Senegal')),
('SO', _('Somalia')),
('SR', _('Suriname')),
('ST', _('Sao Tome & Principe')),
('SV', _('El Salvador')),
('SY', _('Syrian Arab Republic')),
('SZ', _('Swaziland')),
('TC', _('Turks & Caicos Islands')),
('TD', _('Chad')),
('TF', _('French Southern Territories')),
('TG', _('Togo')),
('TH', _('Thailand')),
('TJ', _('Tajikistan')),
('TK', _('Tokelau')),
('TM', _('Turkmenistan')),
('TN', _('Tunisia')),
('TO', _('Tonga')),
('TP', _('East Timor')),
('TR', _('Turkey')),
('TT', _('Trinidad & Tobago')),
('TV', _('Tuvalu')),
('TW', _('Taiwan, Province of China')),
('TZ', _('Tanzania, United Republic of')),
('UA', _('Ukraine')),
('UG', _('Uganda')),
('UM', _('United States Minor Outlying Islands')),
('US', _('United States of America')),
('UY', _('Uruguay')),
('UZ', _('Uzbekistan')),
('VA', _('Vatican City State (Holy See)')),
('VC', _('St. Vincent & the Grenadines')),
('VE', _('Venezuela')),
('VG', _('British Virgin Islands')),
('VI', _('United States Virgin Islands')),
('VN', _('Viet Nam')),
('VU', _('Vanuatu')),
('WF', _('Wallis & Futuna Islands')),
('WS', _('Samoa')),
('YE', _('Yemen')),
('YT', _('Mayotte')),
('YU', _('Yugoslavia')),
('ZA', _('South Africa')),
('ZM', _('Zambia')),
('ZR', _('Zaire')),
('ZW', _('Zimbabwe')),
)

6
uncloud/admin.py Normal file
View File

@ -0,0 +1,6 @@
from django.contrib import admin
from .models import UncloudProvider
for m in [ UncloudProvider ]:
admin.site.register(m)

View File

@ -7,6 +7,8 @@ from django.contrib.auth import get_user_model
from django.conf import settings from django.conf import settings
from uncloud_pay.models import BillingAddress, RecurringPeriod, Product from uncloud_pay.models import BillingAddress, RecurringPeriod, Product
from uncloud_net.models import UncloudNetwork
from uncloud.models import UncloudProvider
class Command(BaseCommand): class Command(BaseCommand):
@ -37,3 +39,6 @@ class Command(BaseCommand):
BillingAddress.populate_db_defaults() BillingAddress.populate_db_defaults()
RecurringPeriod.populate_db_defaults() RecurringPeriod.populate_db_defaults()
Product.populate_db_defaults() Product.populate_db_defaults()
UncloudNetwork.populate_db_defaults()
UncloudProvider.populate_db_defaults()

View File

@ -0,0 +1,24 @@
# Generated by Django 3.1 on 2020-10-11 19:59
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='UncloudProvider',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('starting_date', models.DateField()),
('ending_date', models.DateField(blank=True)),
('name', models.CharField(max_length=256)),
('address', models.TextField()),
],
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.1 on 2020-10-11 20:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('uncloud', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='uncloudprovider',
name='ending_date',
field=models.DateField(blank=True, null=True),
),
]

View File

@ -0,0 +1,27 @@
# Generated by Django 3.1 on 2020-10-11 20:09
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('uncloud_net', '0010_auto_20201011_2009'),
('uncloud', '0002_auto_20201011_2001'),
]
operations = [
migrations.AddField(
model_name='uncloudprovider',
name='billing_network',
field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, related_name='uncloudproviderbill', to='uncloud_net.uncloudnetwork'),
preserve_default=False,
),
migrations.AddField(
model_name='uncloudprovider',
name='referral_network',
field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, related_name='uncloudproviderreferral', to='uncloud_net.uncloudnetwork'),
preserve_default=False,
),
]

File diff suppressed because one or more lines are too long

View File

View File

@ -1,8 +1,12 @@
from django.db import models from django.db import models
from django.db.models import JSONField from django.db.models import JSONField, Q
from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from uncloud import COUNTRIES
from uncloud_net.models import UncloudNetwork
class UncloudModel(models.Model): class UncloudModel(models.Model):
""" """
This class extends the standard model with an This class extends the standard model with an
@ -34,3 +38,80 @@ class UncloudStatus(models.TextChoices):
DELETED = 'DELETED', _('Deleted') # Resource has been deleted DELETED = 'DELETED', _('Deleted') # Resource has been deleted
DISABLED = 'DISABLED', _('Disabled') # Is usable, but cannot be used for new things DISABLED = 'DISABLED', _('Disabled') # Is usable, but cannot be used for new things
UNUSABLE = 'UNUSABLE', _('Unusable'), # Has some kind of error UNUSABLE = 'UNUSABLE', _('Unusable'), # Has some kind of error
###
# General address handling
class CountryField(models.CharField):
def __init__(self, *args, **kwargs):
kwargs.setdefault('choices', COUNTRIES)
kwargs.setdefault('default', 'CH')
kwargs.setdefault('max_length', 2)
super().__init__(*args, **kwargs)
def get_internal_type(self):
return "CharField"
class UncloudAddress(models.Model):
full_name = models.CharField(max_length=256)
organization = models.CharField(max_length=256, blank=True, null=True)
street = models.CharField(max_length=256)
city = models.CharField(max_length=256)
postal_code = models.CharField(max_length=64)
country = CountryField(blank=True)
class Meta:
abstract = True
###
# Who is running / providing this instance of uncloud?
class UncloudProvider(UncloudAddress):
"""
A class resembling who is running this uncloud instance.
This might change over time so we allow starting/ending dates
This also defines the taxation rules.
starting/ending date define from when to when this is valid. This way
we can model address changes and have it correct in the bills.
"""
# Meta:
# FIXMe: only allow non overlapping time frames -- how to define this as a constraint?
starting_date = models.DateField()
ending_date = models.DateField(blank=True, null=True)
billing_network = models.ForeignKey(UncloudNetwork, related_name="uncloudproviderbill", on_delete=models.CASCADE)
referral_network = models.ForeignKey(UncloudNetwork, related_name="uncloudproviderreferral", on_delete=models.CASCADE)
@classmethod
def get_provider(cls, when=None):
"""
Find active provide at a certain time - if there was any
"""
if not when:
when = timezone.now()
return cls.objects.get(Q(starting_date__gte=when, ending_date__lte=when) |
Q(starting_date__gte=when, ending_date__isnull=True))
@classmethod
def populate_db_defaults(cls):
obj, created = cls.objects.get_or_create(name="ungleich glarus ag",
address="Bahnhofstrasse 1\n8783 Linthal\nSwitzerland",
starting_date=timezone.now(),
billing_network=UncloudNetwork.objects.get(description="uncloud Billing"),
referral_network=UncloudNetwork.objects.get(description="uncloud Referral")
)
def __str__(self):
return f"{self.name} {self.address}"

View File

@ -1,3 +1,6 @@
from django.contrib import admin from django.contrib import admin
# Register your models here. from .models import UncloudNetwork
for m in [ UncloudNetwork ]:
admin.site.register(m)

View File

@ -0,0 +1,22 @@
# Generated by Django 3.1 on 2020-10-11 19:20
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('uncloud_net', '0006_auto_20200928_1858'),
]
operations = [
migrations.CreateModel(
name='UncloudNetwork',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('network_address', models.GenericIPAddressField()),
('network_mask', models.IntegerField(validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(128)])),
],
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.1 on 2020-10-11 19:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('uncloud_net', '0007_uncloudnetwork'),
]
operations = [
migrations.AlterField(
model_name='uncloudnetwork',
name='network_address',
field=models.GenericIPAddressField(unique=True),
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 3.1 on 2020-10-11 19:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('uncloud_net', '0008_auto_20201011_1924'),
]
operations = [
migrations.AddField(
model_name='uncloudnetwork',
name='description',
field=models.CharField(default='', max_length=256),
preserve_default=False,
),
]

View File

@ -0,0 +1,21 @@
# Generated by Django 3.1 on 2020-10-11 20:09
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('uncloud_net', '0009_uncloudnetwork_description'),
]
operations = [
migrations.RemoveField(
model_name='vpnnetworkreservation',
name='extra_data',
),
migrations.RemoveField(
model_name='vpnpool',
name='extra_data',
),
]

View File

@ -4,16 +4,49 @@ import ipaddress
from django.db import models from django.db import models
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.core.validators import MinValueValidator, MaxValueValidator from django.core.validators import MinValueValidator, MaxValueValidator
from django.core.exceptions import FieldError
class UncloudNetwork(models.Model):
"""
Storing IP networks
"""
network_address = models.GenericIPAddressField(null=False, unique=True)
network_mask = models.IntegerField(null=False,
validators=[MinValueValidator(0),
MaxValueValidator(128)]
)
description = models.CharField(max_length=256)
@classmethod
def populate_db_defaults(cls):
for net, desc in [
( "2a0a:e5c0:11::", "uncloud Billing" ),
( "2a0a:e5c0:11:1::", "uncloud Referral" )
]:
obj, created = cls.objects.get_or_create(network_address=net,
defaults= {
'network_mask': 64,
'description': desc
}
)
from uncloud_pay.models import Product, RecurringPeriod def save(self, *args, **kwargs):
from uncloud.models import UncloudModel, UncloudStatus if not ':' in self.network_address and self.network_mask > 32:
raise FieldError("Mask cannot exceed 32 for IPv4")
super().save(*args, **kwargs)
def __str__(self):
return f"{self.network_address}/{self.network_mask} {self.description}"
class MACAdress(models.Model): class MACAdress(models.Model):
default_prefix = 0x420000000000 default_prefix = 0x420000000000
class VPNPool(UncloudModel): class VPNPool(models.Model):
""" """
Network address pools from which VPNs can be created Network address pools from which VPNs can be created
""" """
@ -145,7 +178,7 @@ AllowedIPs = {peer_network}
pass pass
class VPNNetworkReservation(UncloudModel): class VPNNetworkReservation(models.Model):
""" """
This class tracks the used VPN networks. It will be deleted, when the product is cancelled. This class tracks the used VPN networks. It will be deleted, when the product is cancelled.
""" """

View File

@ -3,13 +3,20 @@ from rest_framework.test import APIRequestFactory, force_authenticate
from rest_framework.reverse import reverse from rest_framework.reverse import reverse
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError, FieldError
from .views import * from .views import *
from .models import * from .models import *
from uncloud_pay.models import BillingAddress, Order from uncloud_pay.models import BillingAddress, Order
class UncloudNetworkTests(TestCase):
def test_invalid_IPv4_network(self):
with self.assertRaises(FieldError):
UncloudNetwork.objects.create(network_address="192.168.1.0",
network_mask=33)
class VPNTests(TestCase): class VPNTests(TestCase):
def setUp(self): def setUp(self):
self.user = get_user_model().objects.create_user('django-test-user', 'noreply@ungleich.ch') self.user = get_user_model().objects.create_user('django-test-user', 'noreply@ungleich.ch')

View File

@ -1,4 +1,4 @@
from django.utils.translation import gettext_lazy as _
import decimal import decimal
# Define DecimalField properties, used to represent amounts of money. # Define DecimalField properties, used to represent amounts of money.
@ -6,245 +6,3 @@ AMOUNT_MAX_DIGITS=10
AMOUNT_DECIMALS=2 AMOUNT_DECIMALS=2
decimal.getcontext().prec = AMOUNT_DECIMALS decimal.getcontext().prec = AMOUNT_DECIMALS
# http://xml.coverpages.org/country3166.html
COUNTRIES = (
('AD', _('Andorra')),
('AE', _('United Arab Emirates')),
('AF', _('Afghanistan')),
('AG', _('Antigua & Barbuda')),
('AI', _('Anguilla')),
('AL', _('Albania')),
('AM', _('Armenia')),
('AN', _('Netherlands Antilles')),
('AO', _('Angola')),
('AQ', _('Antarctica')),
('AR', _('Argentina')),
('AS', _('American Samoa')),
('AT', _('Austria')),
('AU', _('Australia')),
('AW', _('Aruba')),
('AZ', _('Azerbaijan')),
('BA', _('Bosnia and Herzegovina')),
('BB', _('Barbados')),
('BD', _('Bangladesh')),
('BE', _('Belgium')),
('BF', _('Burkina Faso')),
('BG', _('Bulgaria')),
('BH', _('Bahrain')),
('BI', _('Burundi')),
('BJ', _('Benin')),
('BM', _('Bermuda')),
('BN', _('Brunei Darussalam')),
('BO', _('Bolivia')),
('BR', _('Brazil')),
('BS', _('Bahama')),
('BT', _('Bhutan')),
('BV', _('Bouvet Island')),
('BW', _('Botswana')),
('BY', _('Belarus')),
('BZ', _('Belize')),
('CA', _('Canada')),
('CC', _('Cocos (Keeling) Islands')),
('CF', _('Central African Republic')),
('CG', _('Congo')),
('CH', _('Switzerland')),
('CI', _('Ivory Coast')),
('CK', _('Cook Iislands')),
('CL', _('Chile')),
('CM', _('Cameroon')),
('CN', _('China')),
('CO', _('Colombia')),
('CR', _('Costa Rica')),
('CU', _('Cuba')),
('CV', _('Cape Verde')),
('CX', _('Christmas Island')),
('CY', _('Cyprus')),
('CZ', _('Czech Republic')),
('DE', _('Germany')),
('DJ', _('Djibouti')),
('DK', _('Denmark')),
('DM', _('Dominica')),
('DO', _('Dominican Republic')),
('DZ', _('Algeria')),
('EC', _('Ecuador')),
('EE', _('Estonia')),
('EG', _('Egypt')),
('EH', _('Western Sahara')),
('ER', _('Eritrea')),
('ES', _('Spain')),
('ET', _('Ethiopia')),
('FI', _('Finland')),
('FJ', _('Fiji')),
('FK', _('Falkland Islands (Malvinas)')),
('FM', _('Micronesia')),
('FO', _('Faroe Islands')),
('FR', _('France')),
('FX', _('France, Metropolitan')),
('GA', _('Gabon')),
('GB', _('United Kingdom (Great Britain)')),
('GD', _('Grenada')),
('GE', _('Georgia')),
('GF', _('French Guiana')),
('GH', _('Ghana')),
('GI', _('Gibraltar')),
('GL', _('Greenland')),
('GM', _('Gambia')),
('GN', _('Guinea')),
('GP', _('Guadeloupe')),
('GQ', _('Equatorial Guinea')),
('GR', _('Greece')),
('GS', _('South Georgia and the South Sandwich Islands')),
('GT', _('Guatemala')),
('GU', _('Guam')),
('GW', _('Guinea-Bissau')),
('GY', _('Guyana')),
('HK', _('Hong Kong')),
('HM', _('Heard & McDonald Islands')),
('HN', _('Honduras')),
('HR', _('Croatia')),
('HT', _('Haiti')),
('HU', _('Hungary')),
('ID', _('Indonesia')),
('IE', _('Ireland')),
('IL', _('Israel')),
('IN', _('India')),
('IO', _('British Indian Ocean Territory')),
('IQ', _('Iraq')),
('IR', _('Islamic Republic of Iran')),
('IS', _('Iceland')),
('IT', _('Italy')),
('JM', _('Jamaica')),
('JO', _('Jordan')),
('JP', _('Japan')),
('KE', _('Kenya')),
('KG', _('Kyrgyzstan')),
('KH', _('Cambodia')),
('KI', _('Kiribati')),
('KM', _('Comoros')),
('KN', _('St. Kitts and Nevis')),
('KP', _('Korea, Democratic People\'s Republic of')),
('KR', _('Korea, Republic of')),
('KW', _('Kuwait')),
('KY', _('Cayman Islands')),
('KZ', _('Kazakhstan')),
('LA', _('Lao People\'s Democratic Republic')),
('LB', _('Lebanon')),
('LC', _('Saint Lucia')),
('LI', _('Liechtenstein')),
('LK', _('Sri Lanka')),
('LR', _('Liberia')),
('LS', _('Lesotho')),
('LT', _('Lithuania')),
('LU', _('Luxembourg')),
('LV', _('Latvia')),
('LY', _('Libyan Arab Jamahiriya')),
('MA', _('Morocco')),
('MC', _('Monaco')),
('MD', _('Moldova, Republic of')),
('MG', _('Madagascar')),
('MH', _('Marshall Islands')),
('ML', _('Mali')),
('MN', _('Mongolia')),
('MM', _('Myanmar')),
('MO', _('Macau')),
('MP', _('Northern Mariana Islands')),
('MQ', _('Martinique')),
('MR', _('Mauritania')),
('MS', _('Monserrat')),
('MT', _('Malta')),
('MU', _('Mauritius')),
('MV', _('Maldives')),
('MW', _('Malawi')),
('MX', _('Mexico')),
('MY', _('Malaysia')),
('MZ', _('Mozambique')),
('NA', _('Namibia')),
('NC', _('New Caledonia')),
('NE', _('Niger')),
('NF', _('Norfolk Island')),
('NG', _('Nigeria')),
('NI', _('Nicaragua')),
('NL', _('Netherlands')),
('NO', _('Norway')),
('NP', _('Nepal')),
('NR', _('Nauru')),
('NU', _('Niue')),
('NZ', _('New Zealand')),
('OM', _('Oman')),
('PA', _('Panama')),
('PE', _('Peru')),
('PF', _('French Polynesia')),
('PG', _('Papua New Guinea')),
('PH', _('Philippines')),
('PK', _('Pakistan')),
('PL', _('Poland')),
('PM', _('St. Pierre & Miquelon')),
('PN', _('Pitcairn')),
('PR', _('Puerto Rico')),
('PT', _('Portugal')),
('PW', _('Palau')),
('PY', _('Paraguay')),
('QA', _('Qatar')),
('RE', _('Reunion')),
('RO', _('Romania')),
('RU', _('Russian Federation')),
('RW', _('Rwanda')),
('SA', _('Saudi Arabia')),
('SB', _('Solomon Islands')),
('SC', _('Seychelles')),
('SD', _('Sudan')),
('SE', _('Sweden')),
('SG', _('Singapore')),
('SH', _('St. Helena')),
('SI', _('Slovenia')),
('SJ', _('Svalbard & Jan Mayen Islands')),
('SK', _('Slovakia')),
('SL', _('Sierra Leone')),
('SM', _('San Marino')),
('SN', _('Senegal')),
('SO', _('Somalia')),
('SR', _('Suriname')),
('ST', _('Sao Tome & Principe')),
('SV', _('El Salvador')),
('SY', _('Syrian Arab Republic')),
('SZ', _('Swaziland')),
('TC', _('Turks & Caicos Islands')),
('TD', _('Chad')),
('TF', _('French Southern Territories')),
('TG', _('Togo')),
('TH', _('Thailand')),
('TJ', _('Tajikistan')),
('TK', _('Tokelau')),
('TM', _('Turkmenistan')),
('TN', _('Tunisia')),
('TO', _('Tonga')),
('TP', _('East Timor')),
('TR', _('Turkey')),
('TT', _('Trinidad & Tobago')),
('TV', _('Tuvalu')),
('TW', _('Taiwan, Province of China')),
('TZ', _('Tanzania, United Republic of')),
('UA', _('Ukraine')),
('UG', _('Uganda')),
('UM', _('United States Minor Outlying Islands')),
('US', _('United States of America')),
('UY', _('Uruguay')),
('UZ', _('Uzbekistan')),
('VA', _('Vatican City State (Holy See)')),
('VC', _('St. Vincent & the Grenadines')),
('VE', _('Venezuela')),
('VG', _('British Virgin Islands')),
('VI', _('United States Virgin Islands')),
('VN', _('Viet Nam')),
('VU', _('Vanuatu')),
('WF', _('Wallis & Futuna Islands')),
('WS', _('Samoa')),
('YE', _('Yemen')),
('YT', _('Mayotte')),
('YU', _('Yugoslavia')),
('ZA', _('South Africa')),
('ZM', _('Zambia')),
('ZR', _('Zaire')),
('ZW', _('Zimbabwe')),
)

View File

@ -8,7 +8,7 @@ import io
class Command(BaseCommand): class Command(BaseCommand):
help = '''Imports VAT Rates. Assume vat rates of format https://github.com/kdeldycke/vat-rates/blob/master/vat_rates.csv''' help = '''Imports VAT Rates. Assume vat rates of format https://github.com/kdeldycke/vat-rates/blob/master/vat_rates.csv'''
vat_url = "https://raw.githubusercontent.com/kdeldycke/vat-rates/main/vat_rates.csv" vat_url = "https://raw.githubusercontent.com/ungleich/vat-rates/main/vat_rates.csv"
def add_arguments(self, parser): def add_arguments(self, parser):

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,20 @@
# Generated by Django 3.1 on 2020-10-11 20:03
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('uncloud_pay', '0032_uncloudprovider'),
]
operations = [
migrations.RemoveField(
model_name='product',
name='extra_data',
),
migrations.DeleteModel(
name='UncloudProvider',
),
]

View File

@ -0,0 +1,37 @@
# Generated by Django 3.1 on 2020-10-11 20:31
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('uncloud_pay', '0033_auto_20201011_2003'),
]
operations = [
migrations.RemoveField(
model_name='billingaddress',
name='city',
),
migrations.RemoveField(
model_name='billingaddress',
name='country',
),
migrations.RemoveField(
model_name='billingaddress',
name='name',
),
migrations.RemoveField(
model_name='billingaddress',
name='organization',
),
migrations.RemoveField(
model_name='billingaddress',
name='postal_code',
),
migrations.RemoveField(
model_name='billingaddress',
name='street',
),
]

View File

@ -18,9 +18,8 @@ from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.conf import settings from django.conf import settings
import uncloud_pay.stripe import uncloud_pay.stripe
from uncloud_pay import AMOUNT_DECIMALS, AMOUNT_MAX_DIGITS, COUNTRIES from uncloud_pay import AMOUNT_DECIMALS, AMOUNT_MAX_DIGITS
from uncloud.models import UncloudModel, UncloudStatus from uncloud.models import UncloudAddress
# Used to generate bill due dates. # Used to generate bill due dates.
BILL_PAYMENT_DELAY=datetime.timedelta(days=10) BILL_PAYMENT_DELAY=datetime.timedelta(days=10)
@ -69,16 +68,6 @@ class Currency(models.TextChoices):
EUR = 'EUR', _('Euro') EUR = 'EUR', _('Euro')
USD = 'USD', _('US Dollar') USD = 'USD', _('US Dollar')
class CountryField(models.CharField):
def __init__(self, *args, **kwargs):
kwargs.setdefault('choices', COUNTRIES)
kwargs.setdefault('default', 'CH')
kwargs.setdefault('max_length', 2)
super().__init__(*args, **kwargs)
def get_internal_type(self):
return "CharField"
def get_balance_for_user(user): def get_balance_for_user(user):
bills = reduce( bills = reduce(
@ -276,12 +265,6 @@ class RecurringPeriod(models.Model):
class BillingAddress(models.Model): class BillingAddress(models.Model):
owner = models.ForeignKey(get_user_model(), on_delete=models.CASCADE) owner = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
organization = models.CharField(max_length=100, blank=True, null=True)
name = models.CharField(max_length=100)
street = models.CharField(max_length=100)
city = models.CharField(max_length=50)
postal_code = models.CharField(max_length=50)
country = CountryField(blank=True)
vat_number = models.CharField(max_length=100, default="", blank=True) vat_number = models.CharField(max_length=100, default="", blank=True)
active = models.BooleanField(default=False) active = models.BooleanField(default=False)
@ -360,7 +343,7 @@ class VATRate(models.Model):
### ###
# Products # Products
class Product(UncloudModel): class Product(models.Model):
""" """
A product is something a user can order. To record the pricing, we A product is something a user can order. To record the pricing, we
create order that define a state in time. create order that define a state in time.
@ -1061,6 +1044,24 @@ class Bill(models.Model):
bill_records = BillRecord.objects.filter(bill=self) bill_records = BillRecord.objects.filter(bill=self)
return sum([ br.sum for br in bill_records ]) return sum([ br.sum for br in bill_records ])
@property
def vat_rate(self):
"""
Handling VAT is a tricky business - thus we only implement the cases
that we clearly now and leave it open to fellow developers to implement
correct handling for other cases.
Case CH:
- If the customer is in .ch -> apply standard rate
- If the customer is in EU AND private -> apply country specific rate
- If the customer is in EU AND business -> do not apply VAT
- If the customer is outside EU and outside CH -> do not apply VAT
"""
provider_country = UncloudProvider.objects.get()
@classmethod @classmethod
def create_bills_for_all_users(cls): def create_bills_for_all_users(cls):
""" """
@ -1218,30 +1219,3 @@ class ProductToRecurringPeriod(models.Model):
def __str__(self): def __str__(self):
return f"{self.product} - {self.recurring_period} (default: {self.is_default})" return f"{self.product} - {self.recurring_period} (default: {self.is_default})"
###
# Who is running / providing this instance of uncloud?
class UncloudProvider(models.Model):
"""
A class resembling who is running this uncloud instance.
This might change over time so we allow starting/ending dates
This also defines the taxation rules.
starting/ending date define from when to when this is valid. This way
we can model address changes and have it correct in the bills.
"""
valid_from = models.DateField()
valid_to = models.DateField(blank=True)
billing_address = models.ForeignKey(BillingAddress, on_delete=models.CASCADE)
@classmethod
def populate_db_defaults(cls):
ba = BillingAddress.objects.get_or_create()
# obj, created = cls.objects.get_or_create(
# valid_from=timezone.now()
# defaults={ 'duration_seconds': seconds })