From 8d8c4d660c6d8f6c3c17e64f3ceef23584e4b083 Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Mon, 28 Sep 2020 21:59:35 +0200 Subject: [PATCH] Can order a generic product now --- .../migrations/0023_auto_20200928_1944.py | 18 ++++ .../migrations/0024_auto_20200928_1945.py | 24 +++++ uncloud_pay/models.py | 37 ++++++- uncloud_pay/tests.py | 102 ++++++++++-------- 4 files changed, 135 insertions(+), 46 deletions(-) create mode 100644 uncloud_pay/migrations/0023_auto_20200928_1944.py create mode 100644 uncloud_pay/migrations/0024_auto_20200928_1945.py diff --git a/uncloud_pay/migrations/0023_auto_20200928_1944.py b/uncloud_pay/migrations/0023_auto_20200928_1944.py new file mode 100644 index 0000000..3eb0010 --- /dev/null +++ b/uncloud_pay/migrations/0023_auto_20200928_1944.py @@ -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', + ), + ] diff --git a/uncloud_pay/migrations/0024_auto_20200928_1945.py b/uncloud_pay/migrations/0024_auto_20200928_1945.py new file mode 100644 index 0000000..6792049 --- /dev/null +++ b/uncloud_pay/migrations/0024_auto_20200928_1945.py @@ -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)]), + ), + ] diff --git a/uncloud_pay/models.py b/uncloud_pay/models.py index adc044f..988bd2c 100644 --- a/uncloud_pay/models.py +++ b/uncloud_pay/models.py @@ -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})" diff --git a/uncloud_pay/tests.py b/uncloud_pay/tests.py index a6ec43e..c341706 100644 --- a/uncloud_pay/tests.py +++ b/uncloud_pay/tests.py @@ -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):