begin refactor product to user orders instead of single order

Signed-off-by: Nico Schottelius <nico@nico-notebook.schottelius.org>
This commit is contained in:
Nico Schottelius 2020-08-09 14:38:10 +02:00
parent ef02cb61fd
commit 8df1d8dc7c
10 changed files with 224 additions and 90 deletions

View file

@ -0,0 +1,23 @@
# Generated by Django 3.1 on 2020-08-09 12:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('uncloud_pay', '0013_auto_20200809_1237'),
('opennebula', '0003_auto_20200808_1953'),
]
operations = [
migrations.RemoveField(
model_name='vm',
name='order',
),
migrations.AddField(
model_name='vm',
name='orders',
field=models.ManyToManyField(to='uncloud_pay.Order'),
),
]

View file

@ -0,0 +1,23 @@
# Generated by Django 3.1 on 2020-08-09 12:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('uncloud_pay', '0013_auto_20200809_1237'),
('uncloud_net', '0003_auto_20200808_1953'),
]
operations = [
migrations.RemoveField(
model_name='vpnnetwork',
name='order',
),
migrations.AddField(
model_name='vpnnetwork',
name='orders',
field=models.ManyToManyField(to='uncloud_pay.Order'),
),
]

View file

@ -49,15 +49,12 @@ class Command(BaseCommand):
} }
) )
# 25206 + SSD # 25206 + SSD
vm25206 = VMProduct(name="25206", cores=1, ram_in_gb=4, owner=user) vm25206 = VMProduct.objects.create(name="one-25206", cores=1, ram_in_gb=4, owner=user)
vm25206.create_order_at(timezone.make_aware(datetime.datetime(2020,3,3))) vm25206.create_order_at(timezone.make_aware(datetime.datetime(2020,3,3)))
vm25206.save()
vm25206_ssd = VMDiskProduct(vm=vm25206, owner=user, size_in_gb=30) # vm25206_ssd = VMDiskProduct.objects.create(vm=vm25206, owner=user, size_in_gb=30)
vm25206_ssd.create_order_at(timezone.make_aware(datetime.datetime(2020,3,3))) # vm25206_ssd.create_order_at(timezone.make_aware(datetime.datetime(2020,3,3)))
vm25206_ssd.save()
# change 1 # change 1
vm25206.cores = 2 vm25206.cores = 2
@ -65,55 +62,53 @@ class Command(BaseCommand):
vm25206.save() vm25206.save()
vm25206.create_or_update_order(when_to_start=timezone.make_aware(datetime.datetime(2020,4,17))) vm25206.create_or_update_order(when_to_start=timezone.make_aware(datetime.datetime(2020,4,17)))
sys.exit(0)
# change 2 # change 2
vm25206_ssd.size_in_gb = 50 # vm25206_ssd.size_in_gb = 50
vm25206_ssd.save() # vm25206_ssd.save()
vm25206_ssd.create_or_update_order(when_to_start=timezone.make_aware(datetime.datetime(2020,8,5))) # vm25206_ssd.create_or_update_order(when_to_start=timezone.make_aware(datetime.datetime(2020,8,5)))
# 25206 done. # 25206 done.
# 25615 # 25615
vm25615 = VMProduct(name="25615", cores=1, ram_in_gb=4, owner=user) vm25615 = VMProduct.objects.create(name="one-25615", cores=1, ram_in_gb=4, owner=user)
vm25615.create_order_at(timezone.make_aware(datetime.datetime(2020,3,3))) vm25615.create_order_at(timezone.make_aware(datetime.datetime(2020,3,3)))
vm25615.save()
vm25615_ssd = VMDiskProduct(vm=vm25615, owner=user, size_in_gb=30)
vm25615_ssd.create_order_at(timezone.make_aware(datetime.datetime(2020,3,3)))
vm25615_ssd.save()
Bill.create_next_bill_for_user(user)
sys.exit(0)
# Change 2020-04-17
vm25615.cores = 2 vm25615.cores = 2
vm25615.ram_in_gb = 8 vm25615.ram_in_gb = 8
vm25615.save() vm25615.save()
vm25615.create_or_update_order(when_to_start=timezone.make_aware(datetime.datetime(2020,4,17))) vm25615.create_or_update_order(when_to_start=timezone.make_aware(datetime.datetime(2020,4,17)))
# vm25615_ssd = VMDiskProduct(vm=vm25615, owner=user, size_in_gb=30)
# vm25615_ssd.create_order_at(timezone.make_aware(datetime.datetime(2020,3,3)))
# vm25615_ssd.save()
vm25208 = VMProduct.objects.create(name="one-25208", cores=1, ram_in_gb=4, owner=user)
vm25208.create_order_at(timezone.make_aware(datetime.datetime(2020,3,5)))
vm25208.cores = 2
vm25208.ram_in_gb = 8
vm25208.save()
vm25208.create_or_update_order(when_to_start=timezone.make_aware(datetime.datetime(2020,4,17)))
Bill.create_next_bills_for_user(user, ending_date=end_of_month(timezone.make_aware(datetime.datetime(2020,7,31))))
sys.exit(0)
vm25615_ssd.size_in_gb = 50 vm25615_ssd.size_in_gb = 50
vm25615_ssd.save() vm25615_ssd.save()
vm25615_ssd.create_or_update_order(when_to_start=timezone.make_aware(datetime.datetime(2020,8,5))) vm25615_ssd.create_or_update_order(when_to_start=timezone.make_aware(datetime.datetime(2020,8,5)))
vm25208 = VMProduct.objects.create(name="OpenNebula 25208",
cores=1,
ram_in_gb=4,
owner=user)
vm25208_ssd = VMDiskProduct.objects.create(vm=vm25208, vm25208_ssd = VMDiskProduct.objects.create(vm=vm25208,
owner=user, owner=user,
size_in_gb=30) size_in_gb=30)
vm25208.cores = 2
vm25208.ram_in_gb = 8
vm25208.save()
vm25208.create_or_update_order(when_to_start=timezone.make_aware(datetime.datetime(2020,4,17)))
vm25208_ssd.size_in_gb = 50 vm25208_ssd.size_in_gb = 50
vm25208_ssd.save() vm25208_ssd.save()

View file

@ -0,0 +1,40 @@
# Generated by Django 3.1 on 2020-08-09 12:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('uncloud_pay', '0012_auto_20200809_1026'),
]
operations = [
migrations.RemoveField(
model_name='sampleonetimeproduct',
name='order',
),
migrations.RemoveField(
model_name='samplerecurringproduct',
name='order',
),
migrations.RemoveField(
model_name='samplerecurringproductonetimefee',
name='order',
),
migrations.AddField(
model_name='sampleonetimeproduct',
name='orders',
field=models.ManyToManyField(to='uncloud_pay.Order'),
),
migrations.AddField(
model_name='samplerecurringproduct',
name='orders',
field=models.ManyToManyField(to='uncloud_pay.Order'),
),
migrations.AddField(
model_name='samplerecurringproductonetimefee',
name='orders',
field=models.ManyToManyField(to='uncloud_pay.Order'),
),
]

View file

@ -53,6 +53,10 @@ def end_of_this_month():
_, last_day = monthrange(a_day.year, a_day.month) _, last_day = monthrange(a_day.year, a_day.month)
return a_day.replace(day=last_day,hour=23,minute=59,second=59, microsecond=0) return a_day.replace(day=last_day,hour=23,minute=59,second=59, microsecond=0)
def end_before(a_date):
""" Return suitable datetimefield for ending just before a_date """
return a_date - datetime.timedelta(seconds=1)
def default_payment_delay(): def default_payment_delay():
return timezone.now() + BILL_PAYMENT_DELAY return timezone.now() + BILL_PAYMENT_DELAY
@ -363,30 +367,7 @@ class Order(models.Model):
super().save(*args, **kwargs) super().save(*args, **kwargs)
def __str__(self): def __str__(self):
return f"{self.description} (order {self.id})" return f"{self.description} (order={self.id})"
# def active_before(self, ending_date):
# # Was this order started before the specified ending date?
# if self.starting_date <= ending_date:
# if self.ending_date:
# if self.ending_date > ending_date:
# pass
# Termination needs to be verified, maybe also include checking depending orders
# @property
# def is_terminated_now(self):
# return self.is_terminated_at(timezone.now())
# def is_terminated_at(self, a_date):
# return self.ending_date != None and self.ending_date <= a_date
# def terminate(self):
# if not self.is_terminated:
# self.ending_date = timezone.now()
# self.save()
class Bill(models.Model): class Bill(models.Model):
""" """
@ -433,6 +414,14 @@ 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 ])
@classmethod
def create_bills_for_all_users(cls):
"""
Create bills for all users
"""
for owner in get_user_model().objects.all():
cls.create_next_bills_for_user(owner)
@classmethod @classmethod
def create_next_bills_for_user(cls, owner, ending_date=None): def create_next_bills_for_user(cls, owner, ending_date=None):
@ -578,14 +567,6 @@ class Bill(models.Model):
return bill return bill
@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.create_next_bill_for_user(owner)
class BillRecord(models.Model): class BillRecord(models.Model):
""" """
@ -633,10 +614,19 @@ class Product(UncloudModel):
description = "Generic Product" description = "Generic Product"
order = models.ForeignKey(Order, orders = models.ManyToManyField(Order)
on_delete=models.CASCADE, # one_time_order = models.ForeignKey(Order,
editable=True, # on_delete=models.CASCADE,
null=True) # editable=True,
# null=True,
# related_name='product_one_time')
# recurring_order = models.ForeignKey(Order,
# on_delete=models.CASCADE,
# editable=True,
# null=True,
# related_name='product_recurring')
# FIXME: editable=True -> is in the admin, but also editable in DRF # FIXME: editable=True -> is in the admin, but also editable in DRF
status = models.CharField(max_length=32, status = models.CharField(max_length=32,
@ -691,12 +681,10 @@ class Product(UncloudModel):
if not when_to_start: if not when_to_start:
when_to_start = timezone.now() when_to_start = timezone.now()
if not self.order: # Update order = create new order
self.create_order_at(when_to_start, recurring_period) if self.order:
else:
previous_order = self.order previous_order = self.order
when_to_end = when_to_start - datetime.timedelta(seconds=1) when_to_end = end_before(when_to_start)
new_order = Order.objects.create(owner=self.owner, new_order = Order.objects.create(owner=self.owner,
billing_address=self.order.billing_address, billing_address=self.order.billing_address,
@ -706,11 +694,15 @@ class Product(UncloudModel):
description=str(self), description=str(self),
replaces=self.order) replaces=self.order)
print(new_order)
self.order.end_date = when_to_end self.order.end_date = when_to_end
self.order.save() self.order.save()
self.order = new_order self.order = new_order
else:
return self.create_order_at(when_to_start, recurring_period)
@property @property
def recurring_price(self): def recurring_price(self):
""" implement correct values in the child class """ """ implement correct values in the child class """

View file

@ -709,10 +709,8 @@ oAsAAAAAAACGQNAFAAAAAAAAQyDoAgAAAAAAgCEQdAEAAAAAAMAQCLoAAAAAAABgCP83AL6WQ1Y7
{% for record in bill_records %} {% for record in bill_records %}
<tr class="table-list"> <tr class="table-list">
<td>{{ record.starting_date|date:"c" }} <td>{{ record.starting_date|date:"c" }}
{% if record.ending_date %}
- {{ record.ending_date|date:"c" }} - {{ record.ending_date|date:"c" }}
{% endif %} {{ record.order }}
{{ record.order.description }}
</td> </td>
<td>{{ record.quantity|floatformat:2 }}</td> <td>{{ record.quantity|floatformat:2 }}</td>
<td>{{ record.order.price|floatformat:2 }}</td> <td>{{ record.order.price|floatformat:2 }}</td>

View file

@ -6,7 +6,7 @@ from .models import *
from uncloud_service.models import GenericServiceProduct from uncloud_service.models import GenericServiceProduct
class ProductOrderTestCase(TestCase): class ProductTestCase(TestCase):
""" """
Test products and products <-> order interaction Test products and products <-> order interaction
""" """
@ -168,7 +168,7 @@ class BillingAddressTestCase(TestCase):
class BillAndOrderTestCase(TestCase): class BillTestCase(TestCase):
def setUp(self): def setUp(self):
self.user_without_address = get_user_model().objects.create( self.user_without_address = get_user_model().objects.create(
username='no_home_person', username='no_home_person',

View file

@ -0,0 +1,32 @@
# Generated by Django 3.1 on 2020-08-09 12:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('uncloud_pay', '0013_auto_20200809_1237'),
('uncloud_service', '0003_auto_20200808_1953'),
]
operations = [
migrations.RemoveField(
model_name='genericserviceproduct',
name='order',
),
migrations.RemoveField(
model_name='matrixserviceproduct',
name='order',
),
migrations.AddField(
model_name='genericserviceproduct',
name='orders',
field=models.ManyToManyField(to='uncloud_pay.Order'),
),
migrations.AddField(
model_name='matrixserviceproduct',
name='orders',
field=models.ManyToManyField(to='uncloud_pay.Order'),
),
]

View file

@ -0,0 +1,41 @@
# Generated by Django 3.1 on 2020-08-09 12:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('uncloud_pay', '0013_auto_20200809_1237'),
('uncloud_vm', '0003_auto_20200808_1953'),
]
operations = [
migrations.RemoveField(
model_name='vmdiskproduct',
name='order',
),
migrations.RemoveField(
model_name='vmproduct',
name='order',
),
migrations.RemoveField(
model_name='vmsnapshotproduct',
name='order',
),
migrations.AddField(
model_name='vmdiskproduct',
name='orders',
field=models.ManyToManyField(to='uncloud_pay.Order'),
),
migrations.AddField(
model_name='vmproduct',
name='orders',
field=models.ManyToManyField(to='uncloud_pay.Order'),
),
migrations.AddField(
model_name='vmsnapshotproduct',
name='orders',
field=models.ManyToManyField(to='uncloud_pay.Order'),
),
]

View file

@ -58,13 +58,10 @@ class VMProduct(Product):
VMCluster, on_delete=models.CASCADE, editable=False, blank=True, null=True VMCluster, on_delete=models.CASCADE, editable=False, blank=True, null=True
) )
# VM-specific. The name is only intended for customers: it's a pain to
# remember IDs (speaking from experience as ungleich customer)!
name = models.CharField(max_length=32, blank=True, null=True) name = models.CharField(max_length=32, blank=True, null=True)
cores = models.IntegerField() cores = models.IntegerField()
ram_in_gb = models.FloatField() ram_in_gb = models.FloatField()
# Default recurring price is PER_MONTH, see uncloud_pay.models.Product.
@property @property
def recurring_price(self): def recurring_price(self):
return self.cores * 3 + self.ram_in_gb * 4 return self.cores * 3 + self.ram_in_gb * 4
@ -83,14 +80,7 @@ class VMProduct(Product):
def __str__(self): def __str__(self):
name = f"{self.id}" return f"VM id={self.id},name={self.name},cores={self.cores},ram_in_gb={self.ram_in_gb}"
if self.name:
name = f"{self.id} ({self.name})"
return "VM {}: {} cores {} gb ram".format(name,
self.cores,
self.ram_in_gb)
class VMWithOSProduct(VMProduct): class VMWithOSProduct(VMProduct):
@ -165,7 +155,7 @@ class VMDiskProduct(Product):
default=VMDiskType.CEPH_SSD) default=VMDiskType.CEPH_SSD)
def __str__(self): def __str__(self):
return f"Disk {self.size_in_gb}GB ({self.disk_type}) for VM '{self.vm.name}'" return f"Disk {self.size_in_gb}GB ({self.disk_type}) for {self.vm}"
@property @property
def recurring_price(self): def recurring_price(self):