remove big mistake: orders from product

Signed-off-by: Nico Schottelius <nico@nico-notebook.schottelius.org>
This commit is contained in:
Nico Schottelius 2020-09-28 20:44:50 +02:00
commit 1aead50170
8 changed files with 287 additions and 35 deletions

View file

@ -0,0 +1,25 @@
# Generated by Django 3.1 on 2020-09-28 18:44
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('uncloud_pay', '0014_auto_20200825_1915'),
]
operations = [
migrations.RemoveField(
model_name='sampleonetimeproduct',
name='orders',
),
migrations.RemoveField(
model_name='samplerecurringproduct',
name='orders',
),
migrations.RemoveField(
model_name='samplerecurringproductonetimefee',
name='orders',
),
]

View file

@ -354,17 +354,18 @@ class Order(models.Model):
return self.starting_date + datetime.timedelta(seconds=self.recurring_period)
@property
def next_ending_date(self):
def next_cancel_or_downgrade_date(self):
"""
Return the next proper ending date after n times the
recurring_period, where n is an integer.
recurring_period, where n is an integer that applies for downgrading
or cancelling.
"""
if self.recurring_period > 0:
now = timezone.now()
delta = now - self.starting_date
num_times = math.ceil(delta.total_seconds() / self.recurring_period)
num_times = ceil(delta.total_seconds() / self.recurring_period)
next_date = self.starting_date + datetime.timedelta(seconds= num_times * self.recurring_period)
else:
@ -452,8 +453,9 @@ class Order(models.Model):
if self.ending_date and self.ending_date < self.starting_date:
raise ValidationError("End date cannot be before starting date")
if self.ending_date and self.ending_date < self.earliest_ending_date:
raise ValidationError("Ending date is before minimum duration (starting_date + recurring period)")
# do not check this if we upgrade
# if self.ending_date and self.ending_date < self.earliest_ending_date:
# raise ValidationError("Ending date is before minimum duration (starting_date + recurring period)")
super().save(*args, **kwargs)
@ -853,11 +855,9 @@ class BillRecord(models.Model):
class Product(UncloudModel):
"""
A product is something a user orders. To record the pricing, we
A product is something a user can order. To record the pricing, we
create order that define a state in time.
A product can *depend* on other products.
A product can have *one* one_time_order and/or *one*
recurring_order.
@ -872,12 +872,12 @@ class Product(UncloudModel):
description = "Generic Product"
orders = models.ManyToManyField(Order)
status = models.CharField(max_length=32,
choices=UncloudStatus.choices,
default=UncloudStatus.AWAITING_PAYMENT)
# config = models.JSONField()
# Default period for all products
default_recurring_period = RecurringPeriod.PER_30D
@ -941,6 +941,7 @@ class Product(UncloudModel):
self.orders.add(recurring_order)
# FIXME: this could/should be part of Order (?)
def create_or_update_recurring_order(self, when_to_start=None, recurring_period=None):
if not self.recurring_price:
return
@ -954,8 +955,8 @@ class Product(UncloudModel):
if self.last_recurring_order:
if self.recurring_price < self.last_recurring_order.price:
if when_to_start < self.last_recurring_order.next_ending_date:
when_to_start = start_after(self.last_recurring_order.next_ending_date)
if when_to_start < self.last_recurring_order.next_cancel_or_downgrade_date:
when_to_start = start_after(self.last_recurring_order.next_cancel_or_downgrade_date)
when_to_end = end_before(when_to_start)
@ -967,10 +968,9 @@ class Product(UncloudModel):
description=str(self),
replaces=self.last_recurring_order)
self.last_recurring_order.end_date = when_to_end
self.last_recurring_order.replace_with(new_order)
self.orders.add(new_order)
else:
# This might be a bug as it might (re-)create the one time order
self.create_order(when_to_start, recurring_period)
@property

View file

@ -1,6 +1,7 @@
from django.test import TestCase
from django.contrib.auth import get_user_model
from datetime import datetime, date, timedelta
from django.utils import timezone
from .models import *
from uncloud_service.models import GenericServiceProduct
@ -93,7 +94,9 @@ class ProductTestCase(TestCase):
p = SampleRecurringProduct.objects.create(owner=self.user)
p.create_order(timezone.make_aware(datetime.datetime(2020,3,3)))
p.create_or_update_recurring_order(timezone.make_aware(datetime.datetime(2020,3,3)))
p.create_or_update_recurring_order(timezone.make_aware(datetime.datetime(2020,3,4)))
# FIXME: where is the assert?
class BillingAddressTestCase(TestCase):
@ -413,38 +416,96 @@ class ModifyProductTestCase(TestCase):
self.assertNotEqual(bills[0].sum, pro_rata_amount * price)
self.assertEqual(bills[0].sum, price)
def test_bill_for_increasing_product_easy(self):
def test_downgrade_product(self):
"""
Modify product, check general logi
Test downgrading behaviour:
We create a recurring product (recurring time: 30 days) and downgrade after 15 days.
We create the bill right AFTER the end of the first order.
Expected result:
- First bill record for 30 days
- Second bill record starting after 30 days
- Bill contains two bill records
"""
# Create product
starting_date = timezone.make_aware(datetime.datetime(2019,3,3))
user = self.user
starting_price = 10
product = SampleRecurringProduct.objects.create(owner=self.user,
rc_price=starting_price)
downgrade_price = 5
starting_date = timezone.make_aware(datetime.datetime(2019,3,3))
first_order_should_end_at = starting_date + datetime.timedelta(days=30)
change1_date = start_after(starting_date + datetime.timedelta(days=15))
bill_ending_date = change1_date + datetime.timedelta(days=1)
product = SampleRecurringProduct.objects.create(owner=user, rc_price=starting_price)
product.create_order(starting_date)
change1_date = timezone.make_aware(datetime.datetime(2019,4,17))
product.rc_price = 20
product.rc_price = downgrade_price
product.save()
product.create_or_update_recurring_order(when_to_start=change1_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)
bills = Bill.create_next_bills_for_user(user, ending_date=bill_ending_date)
bill = bills[0]
bill_records = BillRecord.objects.filter(bill=bill)
self.assertEqual(len(bill_records), 3)
self.assertEqual(int(bill.sum), 35)
self.assertEqual(len(bill_records), 2)
self.assertEqual(bill_records[0].starting_date, starting_date)
self.assertEqual(bill_records[0].order.ending_date, first_order_should_end_at)
# self.assertEqual(bill_records[0].ending_date, first_order_should_end_at)
# self.assertEqual(bill_records[0].quantity, 1)
# self.assertEqual(bill_records[1].quantity, 1)
# self.assertEqual(int(bill.sum), 15)
def test_upgrade_product(self):
"""
Test upgrading behaviour
"""
user = self.user
# Create product
starting_date = timezone.make_aware(datetime.datetime(2019,3,3))
starting_price = 10
product = SampleRecurringProduct.objects.create(owner=user, rc_price=starting_price)
product.create_order(starting_date)
change1_date = start_after(starting_date + datetime.timedelta(days=15))
product.rc_price = 20
product.save()
product.create_or_update_recurring_order(when_to_start=change1_date)
bill_ending_date = change1_date + datetime.timedelta(days=1)
bills = Bill.create_next_bills_for_user(user, ending_date=bill_ending_date)
bill = bills[0]
bill_records = BillRecord.objects.filter(bill=bill)
self.assertEqual(len(bill_records), 2)
self.assertEqual(bill_records[0].quantity, .5)
self.assertEqual(bill_records[0].ending_date, end_before(change1_date))
self.assertEqual(bill_records[1].quantity, 1)
self.assertEqual(bill_records[1].starting_date, change1_date)
self.assertEqual(int(bill.sum), 25)
# 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):