forked from uncloud/uncloud
Continue to refactor for shifting logic into the order
This commit is contained in:
parent
077c665c53
commit
d8a7964fed
3 changed files with 161 additions and 91 deletions
|
@ -95,8 +95,8 @@ python manage.py migrate
|
||||||
**** Add it to DNS as vpn-XXX.ungleich.ch
|
**** Add it to DNS as vpn-XXX.ungleich.ch
|
||||||
**** Route a /40 network to its IPv6 address
|
**** Route a /40 network to its IPv6 address
|
||||||
**** Install wireguard on it
|
**** Install wireguard on it
|
||||||
**** TODO Enable wireguard on boot
|
**** TODO [#C] Enable wireguard on boot
|
||||||
**** TODO Create a new VPNPool on uncloud with
|
**** TODO [#C] Create a new VPNPool on uncloud with
|
||||||
***** the network address (selecting from our existing pool)
|
***** the network address (selecting from our existing pool)
|
||||||
***** the network size (/...)
|
***** the network size (/...)
|
||||||
***** the vpn host that provides the network (selecting the created VM)
|
***** the vpn host that provides the network (selecting the created VM)
|
||||||
|
@ -210,3 +210,16 @@ VPNNetworks can be managed by all authenticated users.
|
||||||
|
|
||||||
*** Decision
|
*** Decision
|
||||||
We use integers, because they are easy.
|
We use integers, because they are easy.
|
||||||
|
|
||||||
|
** Milestones :uncloud:
|
||||||
|
*** 1.1 (cleanup 1)
|
||||||
|
****
|
||||||
|
*** 1.0 (initial release)
|
||||||
|
**** TODO Initial Generic product support
|
||||||
|
- Product
|
||||||
|
***** TODO Recurring product support
|
||||||
|
****** TODO Support replacing orders for updates
|
||||||
|
****** TODO [#A] Finish split of bill creation
|
||||||
|
****** TODO [#A] Test the new functions in the Order class
|
||||||
|
***** TODO Generating bill for admins/staff
|
||||||
|
-
|
||||||
|
|
|
@ -372,24 +372,23 @@ class Order(models.Model):
|
||||||
|
|
||||||
return next_date
|
return next_date
|
||||||
|
|
||||||
def get_ending_date_for_bill(bill):
|
def get_ending_date_for_bill(self, bill):
|
||||||
"""
|
"""
|
||||||
Determine the ending date given a specific bill
|
Determine the ending date given a specific bill
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# If the order is quit, charge the final amount (?)
|
# If the order is quit, charge the final amount (?)
|
||||||
if order.ending_date:
|
if self.ending_date:
|
||||||
this_ending_date = order.ending_date
|
this_ending_date = self.ending_date
|
||||||
else:
|
else:
|
||||||
if order.earliest_ending_date > bill.ending_date:
|
if self.earliest_ending_date > bill.ending_date:
|
||||||
this_ending_date = order.earliest_ending_date
|
this_ending_date = self.earliest_ending_date
|
||||||
else:
|
else:
|
||||||
this_ending_date = bill.ending_date
|
this_ending_date = bill.ending_date
|
||||||
|
|
||||||
return this_ending_date
|
return this_ending_date
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def count_billed(self):
|
def count_billed(self):
|
||||||
"""
|
"""
|
||||||
|
@ -458,6 +457,74 @@ class Order(models.Model):
|
||||||
|
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def create_bill_record(self, bill):
|
||||||
|
br = None
|
||||||
|
|
||||||
|
if self.is_one_time:
|
||||||
|
if self.billrecord_set.count() == 0:
|
||||||
|
br = BillRecord.objects.create(bill=bill,
|
||||||
|
order=self,
|
||||||
|
starting_date=self.starting_date,
|
||||||
|
ending_date=self.ending_date)
|
||||||
|
else:
|
||||||
|
br = BillRecord.objects.filter(bill=bill, order=self).first()
|
||||||
|
if br:
|
||||||
|
self.update_bill_record_for_recurring_order(br, bill)
|
||||||
|
|
||||||
|
else:
|
||||||
|
br = self.create_new_bill_record_for_recurring_order(bill)
|
||||||
|
|
||||||
|
return br
|
||||||
|
|
||||||
|
def update_bill_record_for_recurring_order(self,
|
||||||
|
bill_record,
|
||||||
|
bill):
|
||||||
|
"""
|
||||||
|
Possibly update a bill record according to the information in the bill
|
||||||
|
"""
|
||||||
|
|
||||||
|
# If the order has an ending date set, we might need to adjust the bill_record
|
||||||
|
if self.ending_date:
|
||||||
|
if bill_record_for_this_bill.ending_date != self.ending_date:
|
||||||
|
bill_record_for_this_bill.ending_date = self.ending_date
|
||||||
|
|
||||||
|
else:
|
||||||
|
# recurring, not terminated, should go until at least end of bill
|
||||||
|
if bill_record_for_this_bill.ending_date < bill.ending_date:
|
||||||
|
bill_record_for_this_bill.ending_date = bill.ending_date
|
||||||
|
|
||||||
|
bill_record_for_this_bill.save()
|
||||||
|
|
||||||
|
def create_new_bill_record_for_recurring_order(self, bill):
|
||||||
|
"""
|
||||||
|
Create a new bill record
|
||||||
|
"""
|
||||||
|
|
||||||
|
last_bill_record = BillRecord.objects.filter(order=self).order_by('id').last()
|
||||||
|
|
||||||
|
starting_date=self.starting_date
|
||||||
|
|
||||||
|
if last_bill_record:
|
||||||
|
# We already charged beyond the end of this bill's period
|
||||||
|
if last_bill_record.ending_date >= bill.ending_date:
|
||||||
|
return
|
||||||
|
|
||||||
|
# This order is terminated or replaced
|
||||||
|
if self.ending_date:
|
||||||
|
# And the last bill record already covered us -> nothing to be done anymore
|
||||||
|
if last_bill_record.ending_date == self.ending_date:
|
||||||
|
return
|
||||||
|
|
||||||
|
starting_date = start_after(last_bill_record.ending_date)
|
||||||
|
|
||||||
|
ending_date = self.get_ending_date_for_bill(bill)
|
||||||
|
|
||||||
|
return BillRecord.objects.create(bill=bill,
|
||||||
|
order=self,
|
||||||
|
starting_date=starting_date,
|
||||||
|
ending_date=ending_date)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.description} (order={self.id})"
|
return f"{self.description} (order={self.id})"
|
||||||
|
|
||||||
|
@ -509,34 +576,12 @@ class Bill(models.Model):
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_bills_for_all_users(cls):
|
def create_bills_for_all_users(cls):
|
||||||
"""
|
"""
|
||||||
Create bills for all users
|
Create next bill for each user
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for owner in get_user_model().objects.all():
|
for owner in get_user_model().objects.all():
|
||||||
cls.create_next_bills_for_user(owner)
|
cls.create_next_bills_for_user(owner)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create_next_bill_for_user_address(cls,
|
|
||||||
owner,
|
|
||||||
billing_address,
|
|
||||||
ending_date=None):
|
|
||||||
|
|
||||||
"""
|
|
||||||
Check the existing orders of the billing address
|
|
||||||
and generate a bill if there is at least one active order
|
|
||||||
"""
|
|
||||||
|
|
||||||
all_orders = Order.objects.filter(owner=owner,
|
|
||||||
billing_address=billing_address).order_by('id')
|
|
||||||
|
|
||||||
|
|
||||||
# a bill. If there
|
|
||||||
# for order in all_orders:
|
|
||||||
# if order.is_one_time:
|
|
||||||
# cls.create_one_time_record
|
|
||||||
|
|
||||||
# pass
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_next_bills_for_user(cls, owner, ending_date=None):
|
def create_next_bills_for_user(cls, owner, ending_date=None):
|
||||||
"""
|
"""
|
||||||
|
@ -547,80 +592,93 @@ class Bill(models.Model):
|
||||||
bills = []
|
bills = []
|
||||||
|
|
||||||
for billing_address in BillingAddress.objects.filter(owner=owner):
|
for billing_address in BillingAddress.objects.filter(owner=owner):
|
||||||
bills.append(cls.create_next_bill_for_user_address(owner, billing_address, ending_date))
|
bills.append(cls.create_next_bill_for_user_address(billing_address, ending_date))
|
||||||
|
|
||||||
return bills
|
return bills
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_or_create_bill(cls, billing_address):
|
||||||
|
last_bill = cls.objects.filter(billing_address=billing_address).order_by('id').last()
|
||||||
|
|
||||||
|
all_orders = Order.objects.filter(billing_address=billing_address).order_by('id')
|
||||||
|
first_order = all_orders.first()
|
||||||
|
|
||||||
|
bill = None
|
||||||
|
ending_date = None
|
||||||
|
|
||||||
|
# Get date & bill from previous bill, if it exists
|
||||||
|
if last_bill:
|
||||||
|
if not last_bill.is_final:
|
||||||
|
bill = last_bill
|
||||||
|
starting_date = last_bill.starting_date
|
||||||
|
ending_date = bill.ending_date
|
||||||
|
else:
|
||||||
|
starting_date = last_bill.ending_date + datetime.timedelta(seconds=1)
|
||||||
|
else:
|
||||||
|
# Might be an idea to make this the start of the month, too
|
||||||
|
if first_order:
|
||||||
|
starting_date = first_order.starting_date
|
||||||
|
else:
|
||||||
|
starting_date = timezone.now()
|
||||||
|
|
||||||
|
if not ending_date:
|
||||||
|
ending_date = end_of_month(starting_date)
|
||||||
|
|
||||||
|
if not bill:
|
||||||
|
bill = cls.objects.create(
|
||||||
|
owner=billing_address.owner,
|
||||||
|
starting_date=starting_date,
|
||||||
|
ending_date=ending_date,
|
||||||
|
billing_address=billing_address)
|
||||||
|
|
||||||
|
|
||||||
|
return bill
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_bill_records_for_recurring_orders(cls, bill):
|
def create_next_bill_for_user_address(cls,
|
||||||
|
billing_address,
|
||||||
|
ending_date=None):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Create or update bill records for recurring orders for the
|
Create the next bill for a specific billing address of a user
|
||||||
given bill
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
owner = bill.owner
|
owner = billing_address.owner
|
||||||
billing_address = bill.billing_address
|
|
||||||
|
|
||||||
all_orders = Order.objects.filter(owner=owner,
|
all_orders = Order.objects.filter(owner=owner,
|
||||||
billing_address=billing_address).order_by('id').exclude(recurring_period=RecurringPeriod.ONE_TIME)
|
billing_address=billing_address).order_by('id')
|
||||||
|
|
||||||
|
bill = cls.get_or_create_bill(billing_address)
|
||||||
|
|
||||||
for order in all_orders:
|
for order in all_orders:
|
||||||
bill_record_for_this_bill = BillRecord.objects.filter(bill=bill,
|
order.create_bill_record(bill)
|
||||||
order=order).first()
|
|
||||||
if bill_record_for_this_bill:
|
|
||||||
cls.update_bill_record_for_this_bill(bill_record_for_this_bill,
|
|
||||||
order,
|
|
||||||
bill)
|
|
||||||
|
|
||||||
else:
|
return bill
|
||||||
cls.create_new_bill_record_for_this_bill(order, bill)
|
|
||||||
|
|
||||||
|
# @classmethod
|
||||||
|
# def create_bill_records_for_recurring_orders(cls, bill):
|
||||||
|
# """
|
||||||
|
# Create or update bill records for recurring orders for the
|
||||||
|
# given bill
|
||||||
|
# """
|
||||||
|
|
||||||
@staticmethod
|
# owner = bill.owner
|
||||||
def create_new_bill_record_for_this_bill(order, bill):
|
# billing_address = bill.billing_address
|
||||||
last_bill_record = BillRecord.objects.filter(order=order).order_by('id').last()
|
|
||||||
|
|
||||||
this_starting_date=order.starting_date
|
# all_orders = Order.objects.filter(owner=owner,
|
||||||
|
# billing_address=billing_address).order_by('id').exclude(recurring_period=RecurringPeriod.ONE_TIME)
|
||||||
|
|
||||||
if last_bill_record:
|
# for order in all_orders:
|
||||||
if last_bill_record.ending_date >= bill.ending_date:
|
# bill_record_for_this_bill = BillRecord.objects.filter(bill=bill,
|
||||||
return
|
# order=order).first()
|
||||||
|
# if bill_record_for_this_bill:
|
||||||
|
# cls.update_bill_record_for_this_bill(bill_record_for_this_bill,
|
||||||
|
# order,
|
||||||
|
# bill)
|
||||||
|
|
||||||
if order.ending_date:
|
# else:
|
||||||
if last_bill_record.ending_date == order.ending_date:
|
# cls.create_new_bill_record_for_this_bill(order, bill)
|
||||||
return
|
|
||||||
|
|
||||||
this_starting_date = start_after(last_bill_record.ending_date)
|
|
||||||
|
|
||||||
|
|
||||||
ending_date = order.get_ending_date_for_bill(bill)
|
|
||||||
|
|
||||||
return BillRecord.objects.create(bill=bill,
|
|
||||||
order=order,
|
|
||||||
starting_date=this_starting_date,
|
|
||||||
ending_date=this_ending_date)
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def update_bill_record_for_this_bill(bill_record,
|
|
||||||
order,
|
|
||||||
bill):
|
|
||||||
|
|
||||||
# we may need to adjust it, but let's do this logic another time
|
|
||||||
|
|
||||||
# If the order has an ending date set, we might need to adjust the bill_record
|
|
||||||
if order.ending_date:
|
|
||||||
if bill_record_for_this_bill.ending_date != order.ending_date:
|
|
||||||
bill_record_for_this_bill.ending_date = order.ending_date
|
|
||||||
|
|
||||||
else:
|
|
||||||
# recurring, not terminated, should go until at least end of bill
|
|
||||||
if bill_record_for_this_bill.ending_date < bill.ending_date:
|
|
||||||
bill_record_for_this_bill.ending_date = bill.ending_date
|
|
||||||
|
|
||||||
|
|
||||||
bill_record_for_this_bill.save()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_next_bill_for_user_address_old(cls,
|
def create_next_bill_for_user_address_old(cls,
|
||||||
|
|
|
@ -269,7 +269,7 @@ class BillTestCase(TestCase):
|
||||||
Ensure there is only 1 bill record per order
|
Ensure there is only 1 bill record per order
|
||||||
"""
|
"""
|
||||||
|
|
||||||
bill = Bill.create_next_bill_for_user_address(self.user, self.user_addr)
|
bill = Bill.create_next_bill_for_user_address(self.user_addr)
|
||||||
|
|
||||||
self.assertEqual(self.one_time_order.billrecord_set.count(), 1)
|
self.assertEqual(self.one_time_order.billrecord_set.count(), 1)
|
||||||
|
|
||||||
|
@ -278,7 +278,7 @@ class BillTestCase(TestCase):
|
||||||
Check the bill sum for a single one time order
|
Check the bill sum for a single one time order
|
||||||
"""
|
"""
|
||||||
|
|
||||||
bill = Bill.create_next_bill_for_user_address(self.user, self.user_addr)
|
bill = Bill.create_next_bill_for_user_address(self.user_addr)
|
||||||
self.assertEqual(bill.sum, self.order_meta[1]['price'])
|
self.assertEqual(bill.sum, self.order_meta[1]['price'])
|
||||||
|
|
||||||
|
|
||||||
|
@ -287,8 +287,7 @@ class BillTestCase(TestCase):
|
||||||
Ensure there is only 1 bill record per order
|
Ensure there is only 1 bill record per order
|
||||||
"""
|
"""
|
||||||
|
|
||||||
bill = Bill.create_next_bill_for_user_address(self.recurring_user,
|
bill = Bill.create_next_bill_for_user_address(self.recurring_user_addr)
|
||||||
self.recurring_user_addr)
|
|
||||||
|
|
||||||
self.assertEqual(self.recurring_order.billrecord_set.count(), 1)
|
self.assertEqual(self.recurring_order.billrecord_set.count(), 1)
|
||||||
self.assertEqual(bill.billrecord_set.count(), 1)
|
self.assertEqual(bill.billrecord_set.count(), 1)
|
||||||
|
@ -301,7 +300,7 @@ class BillTestCase(TestCase):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for ending_date in self.bill_dates:
|
for ending_date in self.bill_dates:
|
||||||
b = Bill.create_next_bill_for_user_address(self.recurring_user, self.recurring_user_addr, ending_date)
|
b = Bill.create_next_bill_for_user_address(self.recurring_user_addr, ending_date)
|
||||||
b.close()
|
b.close()
|
||||||
|
|
||||||
bill_count = Bill.objects.filter(owner=self.recurring_user).count()
|
bill_count = Bill.objects.filter(owner=self.recurring_user).count()
|
||||||
|
|
Loading…
Reference in a new issue