forked from uncloud/uncloud
begin splitting bill record creation function
This commit is contained in:
parent
18f9a3848a
commit
1c7d81762d
3 changed files with 185 additions and 52 deletions
|
@ -11,7 +11,7 @@ from django.http import FileResponse
|
|||
from django.template.loader import render_to_string
|
||||
|
||||
|
||||
from uncloud_pay.models import Bill, Order, BillRecord, BillingAddress
|
||||
from uncloud_pay.models import Bill, Order, BillRecord, BillingAddress, SampleOneTimeProduct, SampleRecurringProduct, SampleRecurringProductOneTimeFee
|
||||
|
||||
|
||||
class BillRecordInline(admin.TabularInline):
|
||||
|
@ -91,5 +91,7 @@ admin.site.register(Order)
|
|||
admin.site.register(BillRecord)
|
||||
admin.site.register(BillingAddress)
|
||||
|
||||
for m in [ SampleOneTimeProduct, SampleRecurringProduct, SampleRecurringProductOneTimeFee ]:
|
||||
admin.site.register(m)
|
||||
|
||||
#admin.site.register(Order, OrderAdmin)
|
||||
|
|
|
@ -459,19 +459,121 @@ class Bill(models.Model):
|
|||
|
||||
return bills
|
||||
|
||||
|
||||
@classmethod
|
||||
def create_next_bill_for_user_address(cls, owner, billing_address, ending_date=None):
|
||||
def create_bill_records_for_recurring_orders(cls, bill):
|
||||
"""
|
||||
Create or update bill records for recurring orders for the
|
||||
given bill
|
||||
"""
|
||||
|
||||
owner = bill.owner
|
||||
billing_address = bill.billing_address
|
||||
|
||||
all_orders = Order.objects.filter(owner=owner,
|
||||
billing_address=billing_address).order_by('id').exclude(recurring_period=RecurringPeriod.ONE_TIME)
|
||||
|
||||
for order in all_orders:
|
||||
bill_record_for_this_bill = BillRecord.objects.filter(bill=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:
|
||||
cls.create_new_bill_record_for_this_bill(order, bill)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def create_new_bill_record_for_this_bill(order, bill):
|
||||
last_bill_record = BillRecord.objects.filter(order=order).order_by('id').last()
|
||||
|
||||
this_starting_date=order.starting_date
|
||||
|
||||
if last_bill_record:
|
||||
if last_bill_record.ending_date >= bill.ending_date:
|
||||
return
|
||||
|
||||
if order.ending_date:
|
||||
if last_bill_record.ending_date == order.ending_date:
|
||||
return
|
||||
|
||||
this_starting_date = start_after(last_bill_record.ending_date)
|
||||
|
||||
|
||||
ending_date = cls.get_bill_record_ending_date(order, 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()
|
||||
|
||||
@staticmethod
|
||||
def get_bill_record_ending_date(order, bill):
|
||||
"""
|
||||
Determine the ending date of the billing record
|
||||
|
||||
|
||||
"""
|
||||
|
||||
# If the order is quit, charge the final amount (?)
|
||||
if order.ending_date:
|
||||
this_ending_date = order.ending_date
|
||||
else:
|
||||
if order.earliest_ending_date > bill.ending_date:
|
||||
this_ending_date = order.earliest_ending_date
|
||||
else:
|
||||
this_ending_date = bill.ending_date
|
||||
|
||||
return this_ending_date
|
||||
|
||||
@classmethod
|
||||
def create_next_bill_for_user_address(cls,
|
||||
owner,
|
||||
billing_address,
|
||||
ending_date=None):
|
||||
"""
|
||||
Filtering ideas (TBD):
|
||||
If order is replaced, it should not be added anymore if it has been billed "last time"
|
||||
If order has ended and finally charged, do not charge anymore
|
||||
|
||||
Find out the last billrecord for the order, if there is none, bill from the starting date
|
||||
"""
|
||||
|
||||
last_bill = cls.objects.filter(owner=owner, billing_address=billing_address).order_by('id').last()
|
||||
|
||||
# it is important to sort orders here, as bill records will be
|
||||
# created (and listed) in this order
|
||||
all_orders = Order.objects.filter(owner=owner, billing_address=billing_address).order_by('id')
|
||||
all_orders = Order.objects.filter(owner=owner,
|
||||
billing_address=billing_address).order_by('id')
|
||||
first_order = all_orders.first()
|
||||
|
||||
bill = None
|
||||
ending_date = None
|
||||
|
||||
# Get date & bill from previous bill
|
||||
# Get date & bill from previous bill, if it exists
|
||||
if last_bill:
|
||||
if not last_bill.is_final:
|
||||
bill = last_bill
|
||||
|
@ -504,12 +606,10 @@ class Bill(models.Model):
|
|||
starting_date=order.starting_date,
|
||||
ending_date=order.ending_date)
|
||||
|
||||
else:
|
||||
# Bill all recurring orders
|
||||
else: # recurring orders
|
||||
bill_record_for_this_bill = BillRecord.objects.filter(bill=bill,
|
||||
order=order).first()
|
||||
|
||||
|
||||
# This bill already has a bill record for the order
|
||||
# We potentially need to update the ending_date if the ending_date
|
||||
# of the bill changed.
|
||||
|
@ -529,11 +629,8 @@ class Bill(models.Model):
|
|||
|
||||
bill_record_for_this_bill.save()
|
||||
|
||||
else:
|
||||
# No bill record in this bill for the order yet
|
||||
|
||||
# Find out whether it was already billed for the billing period of
|
||||
# this bill
|
||||
else: # No bill record in this bill for this order yet
|
||||
# Find out when billed last time
|
||||
last_bill_record = BillRecord.objects.filter(order=order).order_by('id').last()
|
||||
|
||||
# Default starting date
|
||||
|
@ -580,12 +677,6 @@ class Bill(models.Model):
|
|||
|
||||
|
||||
|
||||
# Filtering ideas:
|
||||
# If order is replaced, it should not be added anymore if it has been billed "last time"
|
||||
# If order has ended and finally charged, do not charge anymore
|
||||
|
||||
# Find out the last billrecord for the order, if there is none, bill from the starting date
|
||||
|
||||
|
||||
return bill
|
||||
|
||||
|
@ -730,8 +821,6 @@ class Product(UncloudModel):
|
|||
when_to_start = timezone.now()
|
||||
|
||||
if self.last_recurring_order:
|
||||
# If the new order is less in value than the previous
|
||||
# order, the previous order needs to be finished first
|
||||
if self.recurring_price < self.last_recurring_order.price:
|
||||
|
||||
if when_to_start < self.last_recurring_order.next_ending_date:
|
||||
|
|
|
@ -414,53 +414,95 @@ class ModifyProductTestCase(TestCase):
|
|||
self.assertNotEqual(bills[0].sum, pro_rata_amount * price)
|
||||
self.assertEqual(bills[0].sum, price)
|
||||
|
||||
def test_bill_for_modified_product(self):
|
||||
def test_bill_for_increasing_product_easy(self):
|
||||
"""
|
||||
Modify a product, see one pro rata entry
|
||||
Modify product, check general logi
|
||||
"""
|
||||
|
||||
price = 5
|
||||
|
||||
# Standard 30d recurring product
|
||||
product = SampleRecurringProduct.objects.create(owner=self.user,
|
||||
rc_price=price)
|
||||
|
||||
# Create product
|
||||
starting_date = timezone.make_aware(datetime.datetime(2019,3,3))
|
||||
ending_date = timezone.make_aware(datetime.datetime(2019,3,31))
|
||||
change_date = timezone.make_aware(datetime.datetime(2019,4,17))
|
||||
|
||||
bill_ending_date = timezone.make_aware(datetime.datetime(2019,4,30))
|
||||
|
||||
starting_price = 10
|
||||
product = SampleRecurringProduct.objects.create(owner=self.user,
|
||||
rc_price=starting_price)
|
||||
product.create_order(starting_date)
|
||||
|
||||
product.rc_price = 10
|
||||
change1_date = timezone.make_aware(datetime.datetime(2019,4,17))
|
||||
product.rc_price = 20
|
||||
product.save()
|
||||
product.create_or_update_recurring_order(when_to_start=change1_date)
|
||||
|
||||
product.create_or_update_recurring_order(when_to_start=change_date)
|
||||
|
||||
bill_ending_date = timezone.make_aware(datetime.datetime(2019,6,30))
|
||||
bills = Bill.create_next_bills_for_user(self.user,
|
||||
ending_date=bill_ending_date)
|
||||
|
||||
# Sum:
|
||||
# recurring1 = 5 CHF -> for 30days
|
||||
# recurring2 = 5 CHF -> from 3rd of April to 3rd of May
|
||||
# recurring3 = 10 CHF -> from 17th of April to ... 17th of May?
|
||||
bill = bills[0]
|
||||
bill_records = BillRecord.objects.filter(bill=bill)
|
||||
|
||||
# If replacing lower order with higher:
|
||||
# - close the lower order NOW
|
||||
# - start higher order NOW+1s
|
||||
# If replacing higher order with lower order:
|
||||
# higher order continues until its end
|
||||
# lower order starts after higher order+1s
|
||||
self.assertEqual(len(bill_records), 3)
|
||||
self.assertEqual(int(bill.sum), 35)
|
||||
|
||||
self.assertEqual(bills[0].sum, price)
|
||||
|
||||
# expeted result:
|
||||
# 1x 5 chf bill record
|
||||
# 1x 5 chf bill record
|
||||
# 1x 10 partial bill record
|
||||
# Expected bill sum & records:
|
||||
# 2019-03-03 - 2019-04-02 +30d: 10
|
||||
# 2019-04-02 - 2019-04-17: +15d: 5
|
||||
# 2019-04-17 - 2019-05-17: +30d: 20
|
||||
# total: 35
|
||||
|
||||
|
||||
# def test_bill_for_increasing_product(self):
|
||||
# """
|
||||
# Modify a product, see one pro rata entry
|
||||
# """
|
||||
|
||||
# # Create product
|
||||
# starting_date = timezone.make_aware(datetime.datetime(2019,3,3))
|
||||
# starting_price = 30.5
|
||||
# product = SampleRecurringProduct.objects.create(owner=self.user,
|
||||
# rc_price=starting_price)
|
||||
# product.create_order(starting_date)
|
||||
|
||||
# recurring_period = product.default_recurring_period.value
|
||||
|
||||
# # First change
|
||||
# change1_date = timezone.make_aware(datetime.datetime(2019,4,17))
|
||||
# product.rc_price = 49.5
|
||||
# product.save()
|
||||
# product.create_or_update_recurring_order(when_to_start=change1_date)
|
||||
|
||||
# # Second change
|
||||
# change2_date = timezone.make_aware(datetime.datetime(2019,5,8))
|
||||
# product.rc_price = 56.5
|
||||
# product.save()
|
||||
# product.create_or_update_recurring_order(when_to_start=change2_date)
|
||||
|
||||
# # Create bill one month after 2nd change
|
||||
# bill_ending_date = timezone.make_aware(datetime.datetime(2019,6,30))
|
||||
# bills = Bill.create_next_bills_for_user(self.user,
|
||||
# ending_date=bill_ending_date)
|
||||
|
||||
# # only one bill in this test case
|
||||
# bill = bills[0]
|
||||
|
||||
# expected_amount = starting_price
|
||||
|
||||
# d2 = starting_date + recurring_period
|
||||
# duration2 = change1_date - d2
|
||||
|
||||
# expected_amount = 0
|
||||
|
||||
# # Expected bill sum & records:
|
||||
# # 2019-03-03 - 2019-04-02 +30d: 30.5
|
||||
# # 2019-04-02 - 2019-04-17: +15d: 15.25
|
||||
# # 2019-04-17 - 2019-05-08: +21d: (21/30) * 49.5
|
||||
# # 2019-05-08 - 2019-06-07: +30d: 56.5
|
||||
# # 2019-06-07 - 2019-07-07: +30d: 56.5
|
||||
|
||||
|
||||
# self.assertEqual(bills[0].sum, price)
|
||||
|
||||
# # expeted result:
|
||||
# # 1x 5 chf bill record
|
||||
# # 1x 5 chf bill record
|
||||
# # 1x 10 partial bill record
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue