forked from uncloud/uncloud
Can order a generic product now
This commit is contained in:
parent
c32499199a
commit
8d8c4d660c
4 changed files with 135 additions and 46 deletions
18
uncloud_pay/migrations/0023_auto_20200928_1944.py
Normal file
18
uncloud_pay/migrations/0023_auto_20200928_1944.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.1 on 2020-09-28 19:44
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('uncloud_pay', '0022_auto_20200928_1932'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='order',
|
||||
old_name='price',
|
||||
new_name='one_time_price',
|
||||
),
|
||||
]
|
24
uncloud_pay/migrations/0024_auto_20200928_1945.py
Normal file
24
uncloud_pay/migrations/0024_auto_20200928_1945.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Generated by Django 3.1 on 2020-09-28 19:45
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('uncloud_pay', '0023_auto_20200928_1944'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='currency',
|
||||
field=models.CharField(choices=[('CHF', 'Swiss Franc'), ('EUR', 'Euro'), ('USD', 'US Dollar')], default='CHF', max_length=32),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='recurring_price',
|
||||
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=10, validators=[django.core.validators.MinValueValidator(0)]),
|
||||
),
|
||||
]
|
|
@ -563,14 +563,22 @@ class Order(models.Model):
|
|||
starting_date = models.DateTimeField(default=timezone.now)
|
||||
ending_date = models.DateTimeField(blank=True, null=True)
|
||||
|
||||
# FIXME: ensure the period is defined in the product
|
||||
recurring_period = models.IntegerField(choices = RecurringPeriod.choices,
|
||||
default = RecurringPeriod.PER_30D)
|
||||
|
||||
price = models.DecimalField(default=0.0,
|
||||
one_time_price = models.DecimalField(default=0.0,
|
||||
max_digits=AMOUNT_MAX_DIGITS,
|
||||
decimal_places=AMOUNT_DECIMALS,
|
||||
validators=[MinValueValidator(0)])
|
||||
|
||||
recurring_price = models.DecimalField(default=0.0,
|
||||
max_digits=AMOUNT_MAX_DIGITS,
|
||||
decimal_places=AMOUNT_DECIMALS,
|
||||
validators=[MinValueValidator(0)])
|
||||
|
||||
currency = models.CharField(max_length=32, choices=Currency.choices, default=Currency.CHF)
|
||||
|
||||
replaces = models.ForeignKey('self',
|
||||
related_name='replaced_by',
|
||||
on_delete=models.CASCADE,
|
||||
|
@ -768,6 +776,33 @@ class Order(models.Model):
|
|||
starting_date=starting_date,
|
||||
ending_date=ending_date)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
one_time_price = 0
|
||||
recurring_price = 0
|
||||
|
||||
# FIXME: support amount independent one time prices
|
||||
# FIXME: support a base price
|
||||
|
||||
if 'features' in self.product.config:
|
||||
for feature in self.product.config['features']:
|
||||
# FIXME: support optional features (?)
|
||||
if not feature in self.config['features']:
|
||||
raise ValidationError(f"Configuration is missing feature {feature}")
|
||||
|
||||
one_time_price += self.product.config['features'][feature]['one_time_price'] * self.config['features'][feature]
|
||||
recurring_price += self.product.config['features'][feature]['recurring_price'] * self.config['features'][feature]
|
||||
|
||||
|
||||
# IMMUTABLE fields -- need to create new order to modify them
|
||||
# However this is not enforced here...
|
||||
if self._state.adding:
|
||||
self.one_time_price = one_time_price
|
||||
self.recurring_price = recurring_price
|
||||
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.description} (order={self.id})"
|
||||
|
||||
|
|
|
@ -8,33 +8,29 @@ from uncloud_service.models import GenericServiceProduct
|
|||
|
||||
import json
|
||||
|
||||
# class OrderTestCase(TestCase):
|
||||
# """
|
||||
# The heart of ordering products
|
||||
# """
|
||||
vm_sample_product_config = {
|
||||
'features': {
|
||||
'cores':
|
||||
{ 'min': 1,
|
||||
'max': 48,
|
||||
'one_time_price': 0,
|
||||
'recurring_price': 4
|
||||
},
|
||||
'ram_gb':
|
||||
{ 'min': 1,
|
||||
'max': 256,
|
||||
'one_time_price': 0,
|
||||
'recurring_price': 4
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
# def setUp(self):
|
||||
# self.user = get_user_model().objects.create(
|
||||
# username='random_user',
|
||||
# email='jane.random@domain.tld')
|
||||
|
||||
# self.ba = BillingAddress.objects.create(
|
||||
# owner=self.user,
|
||||
# organization = 'Test org',
|
||||
# street="unknown",
|
||||
# city="unknown",
|
||||
# postal_code="somewhere else",
|
||||
# active=True)
|
||||
|
||||
# def test_create_one_time_product(self):
|
||||
# """
|
||||
# One time payment products cannot be updated - can they?
|
||||
# """
|
||||
|
||||
# p = SampleOneTimeProduct.objects.create(owner=self.user)
|
||||
|
||||
# self.assertEqual(p.one_time_price, 5)
|
||||
# self.assertEqual(p.recurring_price, 0)
|
||||
vm_sample_order_config = {
|
||||
'features': {
|
||||
'cores': 2,
|
||||
'ram_gb': 2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ProductTestCase(TestCase):
|
||||
|
@ -60,29 +56,45 @@ class ProductTestCase(TestCase):
|
|||
Create a sample product
|
||||
"""
|
||||
|
||||
config = {
|
||||
'features': {
|
||||
'cores':
|
||||
{ 'min': 1,
|
||||
'max': 48,
|
||||
'one_time_price': 0,
|
||||
'recurring_price': 4
|
||||
},
|
||||
'ram_gb':
|
||||
{ 'min': 1,
|
||||
'max': 256,
|
||||
'one_time_price': 0,
|
||||
'recurring_price': 3
|
||||
},
|
||||
},
|
||||
}
|
||||
p = Product.objects.create(name="Testproduct",
|
||||
description="Only for testing",
|
||||
config=vm_sample_product_config)
|
||||
|
||||
|
||||
class OrderTestCase(TestCase):
|
||||
"""
|
||||
The heart of ordering products
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.user = get_user_model().objects.create(
|
||||
username='random_user',
|
||||
email='jane.random@domain.tld')
|
||||
|
||||
self.ba = BillingAddress.objects.create(
|
||||
owner=self.user,
|
||||
organization = 'Test org',
|
||||
street="unknown",
|
||||
city="unknown",
|
||||
postal_code="somewhere else",
|
||||
active=True)
|
||||
|
||||
def test_order_product(self):
|
||||
"""
|
||||
Order a product, ensure the order has correct price setup
|
||||
"""
|
||||
|
||||
p = Product.objects.create(name="Testproduct",
|
||||
description="Only for testing",
|
||||
config=config)
|
||||
config=vm_sample_product_config)
|
||||
|
||||
# self.assertEqual(p.one_time_price, 5)
|
||||
# self.assertEqual(p.recurring_price, 0)
|
||||
o = Order.objects.create(owner=self.user,
|
||||
billing_address=self.ba,
|
||||
product=p,
|
||||
config=vm_sample_order_config)
|
||||
|
||||
self.assertEqual(o.one_time_price, 0)
|
||||
self.assertEqual(o.recurring_price, 16)
|
||||
|
||||
|
||||
# class ProductTestCase(TestCase):
|
||||
|
|
Loading…
Reference in a new issue