update VAT importer
This commit is contained in:
		
					parent
					
						
							
								50fd9e1f37
							
						
					
				
			
			
				commit
				
					
						e03cdf214a
					
				
			
		
					 6 changed files with 141 additions and 79 deletions
				
			
		| 
						 | 
					@ -60,6 +60,22 @@ python manage.py migrate
 | 
				
			||||||
    python manage.py bootstrap-user --username nicocustomer
 | 
					    python manage.py bootstrap-user --username nicocustomer
 | 
				
			||||||
    #+END_SRC
 | 
					    #+END_SRC
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					** Initialise the database
 | 
				
			||||||
 | 
					   While it is not strictly required to add default values to the
 | 
				
			||||||
 | 
					   database, it might significantly reduce the starting time with
 | 
				
			||||||
 | 
					   uncloud.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   To add the default database values run:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   #+BEGIN_SRC shell
 | 
				
			||||||
 | 
					   # Add local objects
 | 
				
			||||||
 | 
					   python manage.py db-add-defaults
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   # Import VAT rates
 | 
				
			||||||
 | 
					   python manage.py import-vat-rates
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   #+END_SRC
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* Testing / CLI Access
 | 
					* Testing / CLI Access
 | 
				
			||||||
  Access via the commandline (CLI) can be done using curl or
 | 
					  Access via the commandline (CLI) can be done using curl or
 | 
				
			||||||
  httpie. In our examples we will use httpie.
 | 
					  httpie. In our examples we will use httpie.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,13 @@
 | 
				
			||||||
from django.core.management.base import BaseCommand
 | 
					import random
 | 
				
			||||||
 | 
					import string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.core.management.base import BaseCommand
 | 
				
			||||||
 | 
					from django.core.exceptions import ObjectDoesNotExist
 | 
				
			||||||
 | 
					from django.contrib.auth import get_user_model
 | 
				
			||||||
 | 
					from django.conf import settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from uncloud_pay.models import BillingAddress, RecurringPeriod, Product
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from uncloud_pay.models import RecurringPeriod, Product
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Command(BaseCommand):
 | 
					class Command(BaseCommand):
 | 
				
			||||||
    help = 'Add standard uncloud values'
 | 
					    help = 'Add standard uncloud values'
 | 
				
			||||||
| 
						 | 
					@ -9,6 +16,24 @@ class Command(BaseCommand):
 | 
				
			||||||
        pass
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def handle(self, *args, **options):
 | 
					    def handle(self, *args, **options):
 | 
				
			||||||
        # Order matters, objects are somewhat dependent on each other
 | 
					        # Order matters, objects can be dependent on each other
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        admin_username="uncloud-admin"
 | 
				
			||||||
 | 
					        pw_length = 32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Only set password if the user did not exist before
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            admin_user = get_user_model().objects.get(username=settings.UNCLOUD_ADMIN_NAME)
 | 
				
			||||||
 | 
					        except ObjectDoesNotExist:
 | 
				
			||||||
 | 
					            random_password = ''.join(random.SystemRandom().choice(string.ascii_lowercase + string.digits) for _ in range(pw_length))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            admin_user = get_user_model().objects.create_user(username=settings.UNCLOUD_ADMIN_NAME, password=random_password)
 | 
				
			||||||
 | 
					            admin_user.is_superuser=True
 | 
				
			||||||
 | 
					            admin_user.is_staff=True
 | 
				
			||||||
 | 
					            admin_user.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            print(f"Created admin user '{admin_username}' with password '{random_password}'")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BillingAddress.populate_db_defaults()
 | 
				
			||||||
        RecurringPeriod.populate_db_defaults()
 | 
					        RecurringPeriod.populate_db_defaults()
 | 
				
			||||||
        Product.populate_db_defaults()
 | 
					        Product.populate_db_defaults()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,8 +19,6 @@ from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGING = {}
 | 
					LOGGING = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
 | 
					# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
 | 
				
			||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 | 
					BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -185,6 +183,8 @@ ALLOWED_HOSTS = []
 | 
				
			||||||
# required for hardcopy / pdf rendering: https://github.com/loftylabs/django-hardcopy
 | 
					# required for hardcopy / pdf rendering: https://github.com/loftylabs/django-hardcopy
 | 
				
			||||||
CHROME_PATH = '/usr/bin/chromium-browser'
 | 
					CHROME_PATH = '/usr/bin/chromium-browser'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Username that is created by default and owns the configuration objects
 | 
				
			||||||
 | 
					UNCLOUD_ADMIN_NAME = "uncloud-admin"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Overwrite settings with local settings, if existing
 | 
					# Overwrite settings with local settings, if existing
 | 
				
			||||||
try:
 | 
					try:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,7 @@ from django.http import FileResponse
 | 
				
			||||||
from django.template.loader import render_to_string
 | 
					from django.template.loader import render_to_string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from uncloud_pay.models import Bill, Order, BillRecord, BillingAddress, Product, RecurringPeriod, ProductToRecurringPeriod
 | 
					from uncloud_pay.models import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class BillRecordInline(admin.TabularInline):
 | 
					class BillRecordInline(admin.TabularInline):
 | 
				
			||||||
| 
						 | 
					@ -88,10 +88,5 @@ admin.site.register(Bill, BillAdmin)
 | 
				
			||||||
admin.site.register(ProductToRecurringPeriod)
 | 
					admin.site.register(ProductToRecurringPeriod)
 | 
				
			||||||
admin.site.register(Product, ProductAdmin)
 | 
					admin.site.register(Product, ProductAdmin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#admin.site.register(Order, OrderAdmin)
 | 
					for m in [ Order, BillRecord, BillingAddress, RecurringPeriod, VATRate ]:
 | 
				
			||||||
#for m in [ SampleOneTimeProduct, SampleRecurringProduct, SampleRecurringProductOneTimeFee ]:
 | 
					    admin.site.register(m)
 | 
				
			||||||
 | 
					 | 
				
			||||||
admin.site.register(Order)
 | 
					 | 
				
			||||||
admin.site.register(BillRecord)
 | 
					 | 
				
			||||||
admin.site.register(BillingAddress)
 | 
					 | 
				
			||||||
admin.site.register(RecurringPeriod)
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,44 +1,35 @@
 | 
				
			||||||
from django.core.management.base import BaseCommand
 | 
					from django.core.management.base import BaseCommand
 | 
				
			||||||
from uncloud_pay.models import VATRate
 | 
					from uncloud_pay.models import VATRate
 | 
				
			||||||
import csv
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import urllib
 | 
				
			||||||
 | 
					import csv
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					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"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def add_arguments(self, parser):
 | 
					    def add_arguments(self, parser):
 | 
				
			||||||
        parser.add_argument('csv_file', nargs='+', type=str)
 | 
					        parser.add_argument('--vat-url', default=self.vat_url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def handle(self, *args, **options):
 | 
					    def handle(self, *args, **options):
 | 
				
			||||||
        try:
 | 
					        vat_url = options['vat_url']
 | 
				
			||||||
            for c_file in options['csv_file']:
 | 
					        url_open = urllib.request.urlopen(vat_url)
 | 
				
			||||||
                print("c_file =  %s" % c_file)
 | 
					
 | 
				
			||||||
                with open(c_file, mode='r') as csv_file:
 | 
					        # map to fileio using stringIO
 | 
				
			||||||
                    csv_reader = csv.DictReader(csv_file)
 | 
					        csv_file = io.StringIO(url_open.read().decode('utf-8'))
 | 
				
			||||||
                    line_count = 0
 | 
					        reader = csv.DictReader(csv_file)
 | 
				
			||||||
                    for row in csv_reader:
 | 
					
 | 
				
			||||||
                        if line_count == 0:
 | 
					        for row in reader:
 | 
				
			||||||
                            line_count += 1
 | 
					#            print(row)
 | 
				
			||||||
            obj, created = VATRate.objects.get_or_create(
 | 
					            obj, created = VATRate.objects.get_or_create(
 | 
				
			||||||
                starting_date=row["start_date"],
 | 
					                starting_date=row["start_date"],
 | 
				
			||||||
                            ending_date=row["stop_date"] if row["stop_date"] is not "" else None,
 | 
					                ending_date=row["stop_date"] if row["stop_date"] != "" else None,
 | 
				
			||||||
                territory_codes=row["territory_codes"],
 | 
					                territory_codes=row["territory_codes"],
 | 
				
			||||||
                currency_code=row["currency_code"],
 | 
					                currency_code=row["currency_code"],
 | 
				
			||||||
                rate=row["rate"],
 | 
					                rate=row["rate"],
 | 
				
			||||||
                rate_type=row["rate_type"],
 | 
					                rate_type=row["rate_type"],
 | 
				
			||||||
                description=row["description"]
 | 
					                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)))
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,28 +1,26 @@
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					import itertools
 | 
				
			||||||
 | 
					import datetime
 | 
				
			||||||
 | 
					from math import ceil
 | 
				
			||||||
 | 
					from calendar import monthrange
 | 
				
			||||||
 | 
					from decimal import Decimal
 | 
				
			||||||
 | 
					from functools import reduce
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.db import models
 | 
					from django.db import models
 | 
				
			||||||
from django.db.models import Q
 | 
					from django.db.models import Q
 | 
				
			||||||
from django.contrib.auth import get_user_model
 | 
					from django.contrib.auth import get_user_model
 | 
				
			||||||
 | 
					from django.contrib.contenttypes.fields import GenericForeignKey
 | 
				
			||||||
 | 
					from django.contrib.contenttypes.models import ContentType
 | 
				
			||||||
from django.utils.translation import gettext_lazy as _
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
from django.core.validators import MinValueValidator
 | 
					from django.core.validators import MinValueValidator
 | 
				
			||||||
from django.utils import timezone
 | 
					from django.utils import timezone
 | 
				
			||||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
 | 
					from django.core.exceptions import ObjectDoesNotExist, ValidationError
 | 
				
			||||||
 | 
					from django.conf import settings
 | 
				
			||||||
from django.contrib.contenttypes.fields import GenericForeignKey
 | 
					 | 
				
			||||||
from django.contrib.contenttypes.models import ContentType
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from functools import reduce
 | 
					 | 
				
			||||||
import itertools
 | 
					 | 
				
			||||||
from math import ceil
 | 
					 | 
				
			||||||
import datetime
 | 
					 | 
				
			||||||
from calendar import monthrange
 | 
					 | 
				
			||||||
from decimal import Decimal
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
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, COUNTRIES
 | 
				
			||||||
from uncloud.models import UncloudModel, UncloudStatus
 | 
					from uncloud.models import UncloudModel, UncloudStatus
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from decimal import Decimal
 | 
					 | 
				
			||||||
import decimal
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 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)
 | 
				
			||||||
| 
						 | 
					@ -102,22 +100,6 @@ class StripeCustomer(models.Model):
 | 
				
			||||||
            on_delete=models.CASCADE)
 | 
					            on_delete=models.CASCADE)
 | 
				
			||||||
    stripe_id = models.CharField(max_length=32)
 | 
					    stripe_id = models.CharField(max_length=32)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###
 | 
					 | 
				
			||||||
# Hosting company configuration
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class HostingProvider(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
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    WIP.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    starting_date = models.DateField()
 | 
					 | 
				
			||||||
    ending_date = models.DateField()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
###
 | 
					###
 | 
				
			||||||
# Payments and Payment Methods.
 | 
					# Payments and Payment Methods.
 | 
				
			||||||
| 
						 | 
					@ -267,7 +249,6 @@ class RecurringPeriod(models.Model):
 | 
				
			||||||
            obj, created = cls.objects.get_or_create(name=name,
 | 
					            obj, created = cls.objects.get_or_create(name=name,
 | 
				
			||||||
                                                     defaults={ 'duration_seconds': seconds })
 | 
					                                                     defaults={ 'duration_seconds': seconds })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def secs_to_name(secs):
 | 
					    def secs_to_name(secs):
 | 
				
			||||||
        name = ""
 | 
					        name = ""
 | 
				
			||||||
| 
						 | 
					@ -290,14 +271,11 @@ class RecurringPeriod(models.Model):
 | 
				
			||||||
        return f"{self.name} ({duration})"
 | 
					        return f"{self.name} ({duration})"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
###
 | 
					###
 | 
				
			||||||
# Bills.
 | 
					# Bills.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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)
 | 
					    organization = models.CharField(max_length=100, blank=True, null=True)
 | 
				
			||||||
    name = models.CharField(max_length=100)
 | 
					    name = models.CharField(max_length=100)
 | 
				
			||||||
    street = models.CharField(max_length=100)
 | 
					    street = models.CharField(max_length=100)
 | 
				
			||||||
| 
						 | 
					@ -314,6 +292,32 @@ class BillingAddress(models.Model):
 | 
				
			||||||
                                    name='one_active_billing_address_per_user')
 | 
					                                    name='one_active_billing_address_per_user')
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def populate_db_defaults(cls):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Ensure we have at least one billing address that is associated with the uncloud-admin.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This way we are sure that an UncloudProvider can be created.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Cannot use get_or_create as that looks for exactly one.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        owner = get_user_model().objects.get(username=settings.UNCLOUD_ADMIN_NAME)
 | 
				
			||||||
 | 
					        billing_address = cls.objects.filter(owner=owner).first()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not billing_address:
 | 
				
			||||||
 | 
					            billing_address = cls.objects.create(owner=owner,
 | 
				
			||||||
 | 
					                                                 organization="uncloud admins",
 | 
				
			||||||
 | 
					                                                 name="Uncloud Admin",
 | 
				
			||||||
 | 
					                                                 street="Uncloudstreet. 42",
 | 
				
			||||||
 | 
					                                                 city="Luchsingen",
 | 
				
			||||||
 | 
					                                                 postal_code="8775",
 | 
				
			||||||
 | 
					                                                 country="CH",
 | 
				
			||||||
 | 
					                                                 active=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def get_address_for(user):
 | 
					    def get_address_for(user):
 | 
				
			||||||
        return BillingAddress.objects.get(owner=user, active=True)
 | 
					        return BillingAddress.objects.get(owner=user, active=True)
 | 
				
			||||||
| 
						 | 
					@ -349,6 +353,10 @@ class VATRate(models.Model):
 | 
				
			||||||
            logger.debug("Did not find VAT rate for %s, returning 0" % country_code)
 | 
					            logger.debug("Did not find VAT rate for %s, returning 0" % country_code)
 | 
				
			||||||
            return 0
 | 
					            return 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return f"{self.territory_codes}: {self.starting_date} - {self.ending_date}: {self.rate_type}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###
 | 
					###
 | 
				
			||||||
# Products
 | 
					# Products
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1205,3 +1213,30 @@ 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 })
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue