Refine tests for bills, multiple bills
This commit is contained in:
parent
70c450afc8
commit
ef02cb61fd
5 changed files with 177 additions and 48 deletions
19
uncloud_pay/migrations/0011_bill_billing_address.py
Normal file
19
uncloud_pay/migrations/0011_bill_billing_address.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 3.1 on 2020-08-09 10:24
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('uncloud_pay', '0010_auto_20200809_0856'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='bill',
|
||||||
|
name='billing_address',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='uncloud_pay.billingaddress'),
|
||||||
|
),
|
||||||
|
]
|
19
uncloud_pay/migrations/0012_auto_20200809_1026.py
Normal file
19
uncloud_pay/migrations/0012_auto_20200809_1026.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 3.1 on 2020-08-09 10:26
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('uncloud_pay', '0011_bill_billing_address'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='bill',
|
||||||
|
name='billing_address',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='uncloud_pay.billingaddress'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -347,6 +347,11 @@ class Order(models.Model):
|
||||||
def is_one_time(self):
|
def is_one_time(self):
|
||||||
return not self.is_recurring
|
return not self.is_recurring
|
||||||
|
|
||||||
|
def replace_with(self, new_order):
|
||||||
|
new_order.replaces = self
|
||||||
|
self.ending_date = new_order.starting_date - datetime.timedelta(seconds=1)
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if self.ending_date and self.ending_date < self.starting_date:
|
if self.ending_date and self.ending_date < self.starting_date:
|
||||||
|
@ -395,6 +400,13 @@ class Bill(models.Model):
|
||||||
ending_date = models.DateTimeField()
|
ending_date = models.DateTimeField()
|
||||||
due_date = models.DateField(default=default_payment_delay)
|
due_date = models.DateField(default=default_payment_delay)
|
||||||
|
|
||||||
|
|
||||||
|
billing_address = models.ForeignKey(BillingAddress,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
editable=True,
|
||||||
|
null=False)
|
||||||
|
# FIXME: editable=True -> is in the admin, but also editable in DRF
|
||||||
|
|
||||||
is_final = models.BooleanField(default=False)
|
is_final = models.BooleanField(default=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -408,10 +420,13 @@ class Bill(models.Model):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Bill {self.owner}-{self.id}"
|
return f"Bill {self.owner}-{self.id}"
|
||||||
|
|
||||||
@property
|
def close(self):
|
||||||
def billing_address(self):
|
"""
|
||||||
pass
|
Close/finish a bill
|
||||||
# if self.order_set.all
|
"""
|
||||||
|
|
||||||
|
self.is_final = True
|
||||||
|
self.save()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sum(self):
|
def sum(self):
|
||||||
|
@ -420,12 +435,26 @@ class Bill(models.Model):
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_next_bill_for_user(cls, owner, ending_date=None):
|
def create_next_bills_for_user(cls, owner, ending_date=None):
|
||||||
last_bill = cls.objects.filter(owner=owner).order_by('id').last()
|
"""
|
||||||
|
Create one bill per billing address, as the VAT rates might be different
|
||||||
|
for each address
|
||||||
|
"""
|
||||||
|
|
||||||
|
bills = []
|
||||||
|
|
||||||
|
for billing_address in BillingAddress.objects.filter(owner=owner):
|
||||||
|
bills.append(cls.create_next_bill_for_user_address(owner, billing_address, ending_date))
|
||||||
|
|
||||||
|
return bills
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_next_bill_for_user_address(cls, owner, billing_address, ending_date=None):
|
||||||
|
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
|
# it is important to sort orders here, as bill records will be
|
||||||
# created (and listed) in this order
|
# created (and listed) in this order
|
||||||
all_orders = Order.objects.filter(owner=owner).order_by('id')
|
all_orders = Order.objects.filter(owner=owner, billing_address=billing_address).order_by('id')
|
||||||
first_order = all_orders.first()
|
first_order = all_orders.first()
|
||||||
|
|
||||||
bill = None
|
bill = None
|
||||||
|
@ -452,7 +481,8 @@ class Bill(models.Model):
|
||||||
bill = cls.objects.create(
|
bill = cls.objects.create(
|
||||||
owner=owner,
|
owner=owner,
|
||||||
starting_date=starting_date,
|
starting_date=starting_date,
|
||||||
ending_date=ending_date)
|
ending_date=ending_date,
|
||||||
|
billing_address=billing_address)
|
||||||
|
|
||||||
for order in all_orders:
|
for order in all_orders:
|
||||||
if order.is_one_time:
|
if order.is_one_time:
|
||||||
|
@ -557,23 +587,6 @@ class Bill(models.Model):
|
||||||
|
|
||||||
cls.create_next_bill_for_user(owner)
|
cls.create_next_bill_for_user(owner)
|
||||||
|
|
||||||
# for order in Order.objects.filter(Q(starting_date__gte=self.starting_date),
|
|
||||||
# Q(starting_date__lte=self.ending_date),
|
|
||||||
|
|
||||||
#for order in Order.objects.filter(owner=owner,
|
|
||||||
# bill_records=None):
|
|
||||||
|
|
||||||
# For each recurring order get the usage and bill it
|
|
||||||
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
"""
|
|
||||||
Close/finish a bill
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.is_final = True
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
class BillRecord(models.Model):
|
class BillRecord(models.Model):
|
||||||
"""
|
"""
|
||||||
Entry of a bill, dynamically generated from an order.
|
Entry of a bill, dynamically generated from an order.
|
||||||
|
@ -657,8 +670,6 @@ class Product(UncloudModel):
|
||||||
one_time_order = None
|
one_time_order = None
|
||||||
|
|
||||||
if recurring_period != RecurringPeriod.ONE_TIME:
|
if recurring_period != RecurringPeriod.ONE_TIME:
|
||||||
print("not one time")
|
|
||||||
|
|
||||||
if one_time_order:
|
if one_time_order:
|
||||||
recurring_order = Order.objects.create(owner=self.owner,
|
recurring_order = Order.objects.create(owner=self.owner,
|
||||||
billing_address=billing_address,
|
billing_address=billing_address,
|
||||||
|
@ -700,15 +711,6 @@ class Product(UncloudModel):
|
||||||
|
|
||||||
self.order = new_order
|
self.order = new_order
|
||||||
|
|
||||||
|
|
||||||
# def save(self, *args, **kwargs):
|
|
||||||
# if not self.order:
|
|
||||||
# raise ValidationError("Cannot create product without order")
|
|
||||||
|
|
||||||
# self.create_or_update_order()
|
|
||||||
|
|
||||||
# super().save(*args, **kwargs)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def recurring_price(self):
|
def recurring_price(self):
|
||||||
""" implement correct values in the child class """
|
""" implement correct values in the child class """
|
||||||
|
@ -724,9 +726,6 @@ class Product(UncloudModel):
|
||||||
def is_recurring(self):
|
def is_recurring(self):
|
||||||
return self.recurring_price > 0
|
return self.recurring_price > 0
|
||||||
|
|
||||||
# on is_one_time as this should be has_one_time which is the same as > 0 again...
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def billing_address(self):
|
def billing_address(self):
|
||||||
return self.order.billing_address
|
return self.order.billing_address
|
||||||
|
|
|
@ -674,7 +674,7 @@ oAsAAAAAAACGQNAFAAAAAAAAQyDoAgAAAAAAgCEQdAEAAAAAAMAQCLoAAAAAAABgCP83AL6WQ1Y7
|
||||||
<div class="d1">
|
<div class="d1">
|
||||||
{% if bill.billing_address.organization != "" %}
|
{% if bill.billing_address.organization != "" %}
|
||||||
<b>ORG{{ bill.billing_address.organization }}</b>
|
<b>ORG{{ bill.billing_address.organization }}</b>
|
||||||
<br>{{ bill.billing_address.name }} <bill.owner.email>
|
<br>{{ bill.billing_address }} <bill.owner.email>
|
||||||
{% else %}
|
{% else %}
|
||||||
<b>{{ bill.billing_address.name }} <bill.owner.email></b>
|
<b>{{ bill.billing_address.name }} <bill.owner.email></b>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -72,6 +72,15 @@ class BillingAddressTestCase(TestCase):
|
||||||
email='jane.random@domain.tld')
|
email='jane.random@domain.tld')
|
||||||
|
|
||||||
|
|
||||||
|
def test_user_no_address(self):
|
||||||
|
"""
|
||||||
|
Raise an error, when there is no address
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.assertRaises(uncloud_pay.models.BillingAddress.DoesNotExist,
|
||||||
|
BillingAddress.get_address_for,
|
||||||
|
self.user)
|
||||||
|
|
||||||
def test_user_only_inactive_address(self):
|
def test_user_only_inactive_address(self):
|
||||||
"""
|
"""
|
||||||
Raise an error, when there is no active address
|
Raise an error, when there is no active address
|
||||||
|
@ -89,7 +98,7 @@ class BillingAddressTestCase(TestCase):
|
||||||
BillingAddress.get_address_for,
|
BillingAddress.get_address_for,
|
||||||
self.user)
|
self.user)
|
||||||
|
|
||||||
def test_user_only_active_address(self):
|
def test_find_active_address(self):
|
||||||
"""
|
"""
|
||||||
Find the active address
|
Find the active address
|
||||||
"""
|
"""
|
||||||
|
@ -105,7 +114,7 @@ class BillingAddressTestCase(TestCase):
|
||||||
|
|
||||||
self.assertEqual(BillingAddress.get_address_for(self.user), ba)
|
self.assertEqual(BillingAddress.get_address_for(self.user), ba)
|
||||||
|
|
||||||
def test_multiple_addresses(self):
|
def test_find_right_address_with_multiple_addresses(self):
|
||||||
"""
|
"""
|
||||||
Find the active address only, skip inactive
|
Find the active address only, skip inactive
|
||||||
"""
|
"""
|
||||||
|
@ -129,6 +138,35 @@ class BillingAddressTestCase(TestCase):
|
||||||
|
|
||||||
self.assertEqual(BillingAddress.get_address_for(self.user), ba)
|
self.assertEqual(BillingAddress.get_address_for(self.user), ba)
|
||||||
|
|
||||||
|
def test_change_addresses(self):
|
||||||
|
"""
|
||||||
|
Switch the active address
|
||||||
|
"""
|
||||||
|
|
||||||
|
ba = BillingAddress.objects.create(
|
||||||
|
owner=self.user,
|
||||||
|
organization = 'Test org',
|
||||||
|
street="unknown",
|
||||||
|
city="unknown",
|
||||||
|
postal_code="unknown",
|
||||||
|
active=True)
|
||||||
|
|
||||||
|
self.assertEqual(BillingAddress.get_address_for(self.user), ba)
|
||||||
|
|
||||||
|
ba.active=False
|
||||||
|
ba.save()
|
||||||
|
|
||||||
|
ba2 = BillingAddress.objects.create(
|
||||||
|
owner=self.user,
|
||||||
|
organization = 'Test org',
|
||||||
|
street="unknown",
|
||||||
|
city="unknown",
|
||||||
|
postal_code="somewhere else",
|
||||||
|
active=True)
|
||||||
|
|
||||||
|
self.assertEqual(BillingAddress.get_address_for(self.user), ba2)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BillAndOrderTestCase(TestCase):
|
class BillAndOrderTestCase(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -144,7 +182,7 @@ class BillAndOrderTestCase(TestCase):
|
||||||
username='recurrent_product_user',
|
username='recurrent_product_user',
|
||||||
email='jane.doe@domain.tld')
|
email='jane.doe@domain.tld')
|
||||||
|
|
||||||
BillingAddress.objects.create(
|
self.user_addr = BillingAddress.objects.create(
|
||||||
owner=self.user,
|
owner=self.user,
|
||||||
organization = 'Test org',
|
organization = 'Test org',
|
||||||
street="unknown",
|
street="unknown",
|
||||||
|
@ -152,7 +190,7 @@ class BillAndOrderTestCase(TestCase):
|
||||||
postal_code="unknown",
|
postal_code="unknown",
|
||||||
active=True)
|
active=True)
|
||||||
|
|
||||||
BillingAddress.objects.create(
|
self.recurring_user_addr = BillingAddress.objects.create(
|
||||||
owner=self.recurring_user,
|
owner=self.recurring_user,
|
||||||
organization = 'Test org',
|
organization = 'Test org',
|
||||||
street="Somewhere",
|
street="Somewhere",
|
||||||
|
@ -194,12 +232,13 @@ class BillAndOrderTestCase(TestCase):
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_bill_one_time_one_bill_record(self):
|
def test_bill_one_time_one_bill_record(self):
|
||||||
"""
|
"""
|
||||||
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(self.user)
|
bill = Bill.create_next_bill_for_user_address(self.user, self.user_addr)
|
||||||
|
|
||||||
self.assertEqual(self.one_time_order.billrecord_set.count(), 1)
|
self.assertEqual(self.one_time_order.billrecord_set.count(), 1)
|
||||||
|
|
||||||
|
@ -208,7 +247,7 @@ class BillAndOrderTestCase(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(self.user)
|
bill = Bill.create_next_bill_for_user_address(self.user, self.user_addr)
|
||||||
self.assertEqual(bill.sum, self.order_meta[1]['price'])
|
self.assertEqual(bill.sum, self.order_meta[1]['price'])
|
||||||
|
|
||||||
|
|
||||||
|
@ -217,7 +256,8 @@ class BillAndOrderTestCase(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(self.recurring_user)
|
bill = Bill.create_next_bill_for_user_address(self.recurring_user,
|
||||||
|
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)
|
||||||
|
@ -230,13 +270,65 @@ class BillAndOrderTestCase(TestCase):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for ending_date in self.bill_dates:
|
for ending_date in self.bill_dates:
|
||||||
b = Bill.create_next_bill_for_user(self.recurring_user, ending_date)
|
b = Bill.create_next_bill_for_user_address(self.recurring_user, 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()
|
||||||
|
|
||||||
self.assertEqual(len(self.bill_dates), bill_count)
|
self.assertEqual(len(self.bill_dates), bill_count)
|
||||||
|
|
||||||
|
def test_multi_addr_multi_bill(self):
|
||||||
|
"""
|
||||||
|
Ensure multiple bills are created if orders exist with different billing addresses
|
||||||
|
"""
|
||||||
|
|
||||||
|
username="lotsofplaces"
|
||||||
|
multi_addr_user = get_user_model().objects.create(
|
||||||
|
username=username,
|
||||||
|
email=f"{username}@example.org")
|
||||||
|
|
||||||
|
user_addr1 = BillingAddress.objects.create(
|
||||||
|
owner=multi_addr_user,
|
||||||
|
organization = 'Test org',
|
||||||
|
street="unknown",
|
||||||
|
city="unknown",
|
||||||
|
postal_code="unknown",
|
||||||
|
active=True)
|
||||||
|
|
||||||
|
order1 = Order.objects.create(
|
||||||
|
owner=multi_addr_user,
|
||||||
|
starting_date=self.order_meta[1]['starting_date'],
|
||||||
|
ending_date=self.order_meta[1]['ending_date'],
|
||||||
|
recurring_period=RecurringPeriod.ONE_TIME,
|
||||||
|
price=self.order_meta[1]['price'],
|
||||||
|
description=self.order_meta[1]['description'],
|
||||||
|
billing_address=BillingAddress.get_address_for(self.user))
|
||||||
|
|
||||||
|
# Make this address inactive
|
||||||
|
user_addr1.active = False
|
||||||
|
user_addr1.save()
|
||||||
|
|
||||||
|
user_addr2 = BillingAddress.objects.create(
|
||||||
|
owner=multi_addr_user,
|
||||||
|
organization = 'Test2 org',
|
||||||
|
street="unknown2",
|
||||||
|
city="unknown2",
|
||||||
|
postal_code="unknown2",
|
||||||
|
active=True)
|
||||||
|
|
||||||
|
order2 = Order.objects.create(
|
||||||
|
owner=multi_addr_user,
|
||||||
|
starting_date=self.order_meta[1]['starting_date'],
|
||||||
|
ending_date=self.order_meta[1]['ending_date'],
|
||||||
|
recurring_period=RecurringPeriod.ONE_TIME,
|
||||||
|
price=self.order_meta[1]['price'],
|
||||||
|
description=self.order_meta[1]['description'],
|
||||||
|
billing_address=BillingAddress.get_address_for(self.user))
|
||||||
|
|
||||||
|
|
||||||
|
bills = Bill.create_next_bills_for_user(multi_addr_user)
|
||||||
|
|
||||||
|
self.assertEqual(len(bills), 2)
|
||||||
|
|
||||||
# class NotABillingTC(TestCase):
|
# class NotABillingTC(TestCase):
|
||||||
# #class BillingTestCase(TestCase):
|
# #class BillingTestCase(TestCase):
|
||||||
|
|
Loading…
Reference in a new issue