From 8decfe1b165101156e5d665c2d76e5346a464b72 Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Sun, 21 Jun 2020 13:46:54 +0200 Subject: [PATCH] Phase in admin, remove uuid from bills --- uncloud/urls.py | 1 + uncloud_pay/admin.py | 11 +- .../migrations/0017_auto_20200621_1110.py | 55 ++++ .../migrations/0018_auto_20200621_1140.py | 25 ++ .../migrations/0019_auto_20200621_1144.py | 23 ++ uncloud_pay/models.py | 248 ++++++++++-------- 6 files changed, 246 insertions(+), 117 deletions(-) create mode 100644 uncloud_pay/migrations/0017_auto_20200621_1110.py create mode 100644 uncloud_pay/migrations/0018_auto_20200621_1140.py create mode 100644 uncloud_pay/migrations/0019_auto_20200621_1144.py diff --git a/uncloud/urls.py b/uncloud/urls.py index 4033f88..b009ec8 100644 --- a/uncloud/urls.py +++ b/uncloud/urls.py @@ -86,4 +86,5 @@ urlpatterns = [ description="uncloud API", version="1.0.0" ), name='openapi-schema'), + path('admin/', admin.site.urls), ] diff --git a/uncloud_pay/admin.py b/uncloud_pay/admin.py index 8c38f3f..0a851b9 100644 --- a/uncloud_pay/admin.py +++ b/uncloud_pay/admin.py @@ -1,3 +1,12 @@ from django.contrib import admin -# Register your models here. +from uncloud_pay.models import Bill, Order, BillRecord, BillingAddress + +class BillAdmin(admin.ModelAdmin): + pass + +admin.site.register(Bill, BillAdmin) + +admin.site.register(Order) +admin.site.register(BillRecord) +admin.site.register(BillingAddress) diff --git a/uncloud_pay/migrations/0017_auto_20200621_1110.py b/uncloud_pay/migrations/0017_auto_20200621_1110.py new file mode 100644 index 0000000..351e242 --- /dev/null +++ b/uncloud_pay/migrations/0017_auto_20200621_1110.py @@ -0,0 +1,55 @@ +# Generated by Django 3.0.6 on 2020-06-21 11:10 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('uncloud_pay', '0016_auto_20200523_2138'), + ] + + operations = [ + migrations.CreateModel( + name='BillRecord', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('usage_count', models.IntegerField(default=1)), + ('creation_date', models.DateTimeField(auto_now_add=True)), + ('starting_date', models.DateTimeField()), + ('ending_date', models.DateTimeField()), + ('bill', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='uncloud_pay.Bill')), + ], + ), + migrations.RemoveField( + model_name='ordertimothee', + name='bill', + ), + migrations.RemoveField( + model_name='ordertimothee', + name='billing_address', + ), + migrations.RemoveField( + model_name='ordertimothee', + name='owner', + ), + migrations.RemoveField( + model_name='order', + name='bill', + ), + migrations.RemoveField( + model_name='order', + name='one_time_price', + ), + migrations.RemoveField( + model_name='order', + name='recurring_price', + ), + migrations.DeleteModel( + name='BillNico', + ), + migrations.DeleteModel( + name='OrderTimothee', + ), + ] diff --git a/uncloud_pay/migrations/0018_auto_20200621_1140.py b/uncloud_pay/migrations/0018_auto_20200621_1140.py new file mode 100644 index 0000000..efc4ff5 --- /dev/null +++ b/uncloud_pay/migrations/0018_auto_20200621_1140.py @@ -0,0 +1,25 @@ +# Generated by Django 3.0.6 on 2020-06-21 11:40 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('uncloud_pay', '0017_auto_20200621_1110'), + ] + + operations = [ + migrations.AddField( + model_name='bill', + name='bill_records', + field=models.ManyToManyField(through='uncloud_pay.BillRecord', to='uncloud_pay.Order'), + ), + migrations.AddField( + model_name='billrecord', + name='order', + field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, to='uncloud_pay.Order'), + preserve_default=False, + ), + ] diff --git a/uncloud_pay/migrations/0019_auto_20200621_1144.py b/uncloud_pay/migrations/0019_auto_20200621_1144.py new file mode 100644 index 0000000..7d5fab9 --- /dev/null +++ b/uncloud_pay/migrations/0019_auto_20200621_1144.py @@ -0,0 +1,23 @@ +# Generated by Django 3.0.6 on 2020-06-21 11:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('uncloud_pay', '0018_auto_20200621_1140'), + ] + + operations = [ + migrations.RemoveField( + model_name='bill', + name='uuid', + ), + migrations.AddField( + model_name='bill', + name='id', + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + preserve_default=False, + ), + ] diff --git a/uncloud_pay/models.py b/uncloud_pay/models.py index bec64e1..cb1537b 100644 --- a/uncloud_pay/models.py +++ b/uncloud_pay/models.py @@ -223,11 +223,14 @@ class BillingAddress(models.Model): return addresses[0] def __str__(self): - return "{}, {}, {} {}, {}".format( - self.name, self.street, self.postal_code, self.city, - self.country) + return "{} - {}, {}, {} {}, {}".format( + self.owner, + self.name, self.street, self.postal_code, self.city, + self.country) + +### +# VAT -# Populated with the import-vat-numbers django command. class VATRate(models.Model): start_date = models.DateField(blank=True, null=True) stop_date = models.DateField(blank=True, null=True) @@ -250,115 +253,6 @@ class VATRate(models.Model): logger.debug("Did not find VAT rate for %s, returning 0" % country_code) return 0 -class BillRecord(models.Model): - """ - Entry of a bill, dynamically generated from an order. - """ - - bill = models.ForeignKey(Bill, on_delete=models.CASCADE) - - # How many times the order has been used in this record - usage_count = models.IntegerField(default=1) - - # The timeframe the bill record is for can (and probably often will) differ - # from the bill time - creation_date = models.DateTimeField(auto_now_add=True) - starting_date = models.DateTimeField() - ending_date = models.DateTimeField() - -class Bill(models.Model): - """ FIXME: - Bill needs to be unique in the triple (owner, year, month) - """ - - uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - owner = models.ForeignKey(get_user_model(), - on_delete=models.CASCADE) - - creation_date = models.DateTimeField(auto_now_add=True) - starting_date = models.DateTimeField() - ending_date = models.DateTimeField() - due_date = models.DateField() - - valid = models.BooleanField(default=True) - - # billing address and vat rate is the same for the whole bill - # @property - # def vat_rate(self): - # return Decimal(VATRate.get_for_country(self.bill.billing_address.country)) - - - @classmethod - def create_all_bills(cls): - for owner in get_user_model().objects.all(): - # mintime = time of first order - # maxtime = time of last order - # iterate month based through it - - cls.assign_orders_to_bill(owner, year, month) - pass - - def assign_orders_to_bill(self, owner, year, month): - """ - Generate a bill for the specific month of a user. - - First handle all one time orders - - FIXME: - - - limit this to active users in the future! (2020-05-23) - """ - - """ - Find all one time orders that have a starting date that falls into this month - recurring_period=RecurringPeriod.ONE_TIME, - - Can we do this even for recurring / all of them - - """ - - # FIXME: add something to check whether the order should be billed at all - i.e. a marker that - # disables searching -> optimization for later - # Create the initial bill record - # FIXME: maybe limit not even to starting/ending date, but to empty_bill record -- to be fixed in the future - # for order in Order.objects.filter(Q(starting_date__gte=self.starting_date), - # Q(starting_date__lte=self.ending_date), - - # FIXME below: only check for active orders - - # Ensure all orders of that owner have at least one bill record - for order in Order.objects.filter(owner=owner, - bill_records=None): - - bill_record = BillRecord.objects.create(bill=self, - usage_count=1, - starting_date=order.starting_date, - ending_date=order.starting_date + timedelta(seconds=order.recurring_period)) - - - # For each recurring order get the usage and bill it - for order in Order.objects.filter(~Q(recurring_period=RecurringPeriod.ONE_TIME), - Q(starting_date__lt=self.starting_date), - owner=owner): - - if order.recurring_period > 0: # avoid div/0 - these are one time payments - - # How much time will have passed by the end of the billing cycle - td = self.ending_date - order.starting_date - - # How MANY times it will have been used by then - used_times = ceil(td / timedelta(seconds=order.recurring_period)) - - billed_times = len(order.bills) - - # How many times it WAS billed -- can also be inferred from the bills that link to it! - if used_times > billed_times: - billing_times = used_times - billed_times - - # ALSO REGISTER THE TIME PERIOD! - pass - - ### # Orders. @@ -382,9 +276,9 @@ class Order(models.Model): ending_date = models.DateTimeField(blank=True, null=True) - bill_records = models.ManyToManyField(BillRecord, - editable=False, - blank=True) + # bill_records = models.ManyToManyField(BillRecord, + # editable=False, + # blank=True) @property def count_billed(self): @@ -512,6 +406,127 @@ class Order(models.Model): self.recurring_price) +class Bill(models.Model): + """ FIXME: + Bill needs to be unique in the triple (owner, year, month) + """ + +# uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + owner = models.ForeignKey(get_user_model(), + on_delete=models.CASCADE) + + creation_date = models.DateTimeField(auto_now_add=True) + starting_date = models.DateTimeField() + ending_date = models.DateTimeField() + due_date = models.DateField() + + valid = models.BooleanField(default=True) + + # Mapping to BillRecords + # https://stackoverflow.com/questions/4443190/djangos-manytomany-relationship-with-additional-fields + + bill_records = models.ManyToManyField(Order, through="BillRecord") + + # billing address and vat rate is the same for the whole bill + # @property + # def vat_rate(self): + # return Decimal(VATRate.get_for_country(self.bill.billing_address.country)) + + + def __str__(self): + return f"uc-{self.id}" + + @classmethod + def create_all_bills(cls): + for owner in get_user_model().objects.all(): + # mintime = time of first order + # maxtime = time of last order + # iterate month based through it + + cls.assign_orders_to_bill(owner, year, month) + pass + + def assign_orders_to_bill(self, owner, year, month): + """ + Generate a bill for the specific month of a user. + + First handle all one time orders + + FIXME: + + - limit this to active users in the future! (2020-05-23) + """ + + """ + Find all one time orders that have a starting date that falls into this month + recurring_period=RecurringPeriod.ONE_TIME, + + Can we do this even for recurring / all of them + + """ + + # FIXME: add something to check whether the order should be billed at all - i.e. a marker that + # disables searching -> optimization for later + # Create the initial bill record + # FIXME: maybe limit not even to starting/ending date, but to empty_bill record -- to be fixed in the future + # for order in Order.objects.filter(Q(starting_date__gte=self.starting_date), + # Q(starting_date__lte=self.ending_date), + + # FIXME below: only check for active orders + + # Ensure all orders of that owner have at least one bill record + for order in Order.objects.filter(owner=owner, + bill_records=None): + + bill_record = BillRecord.objects.create(bill=self, + usage_count=1, + starting_date=order.starting_date, + ending_date=order.starting_date + timedelta(seconds=order.recurring_period)) + + + # For each recurring order get the usage and bill it + for order in Order.objects.filter(~Q(recurring_period=RecurringPeriod.ONE_TIME), + Q(starting_date__lt=self.starting_date), + owner=owner): + + if order.recurring_period > 0: # avoid div/0 - these are one time payments + + # How much time will have passed by the end of the billing cycle + td = self.ending_date - order.starting_date + + # How MANY times it will have been used by then + used_times = ceil(td / timedelta(seconds=order.recurring_period)) + + billed_times = len(order.bills) + + # How many times it WAS billed -- can also be inferred from the bills that link to it! + if used_times > billed_times: + billing_times = used_times - billed_times + + # ALSO REGISTER THE TIME PERIOD! + pass + + +class BillRecord(models.Model): + """ + Entry of a bill, dynamically generated from an order. + """ + + bill = models.ForeignKey(Bill, on_delete=models.CASCADE) + order = models.ForeignKey(Order, on_delete=models.CASCADE) + + # How many times the order has been used in this record + usage_count = models.IntegerField(default=1) + + # The timeframe the bill record is for can (and probably often will) differ + # from the bill time + creation_date = models.DateTimeField(auto_now_add=True) + starting_date = models.DateTimeField() + ending_date = models.DateTimeField() + + def __str__(self): + return f"{order.owner}" + class OrderRecord(models.Model): """ @@ -523,6 +538,7 @@ class OrderRecord(models.Model): """ order = models.ForeignKey(Order, on_delete=models.CASCADE) + one_time_price = models.DecimalField(default=0.0, max_digits=AMOUNT_MAX_DIGITS, decimal_places=AMOUNT_DECIMALS,